text: /*
   *
   *  BlueZ - Bluetooth protocol stack for Linux
   *
   *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
   *
   *
   *  This program is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This program is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   *
   */
  
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  
  #include <stdio.h>
  #include <errno.h>
  #include <unistd.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "parser.h"
  
  static char *opcode2str(uint8_t opcode)
  {
  	switch (opcode & 0x7f) {
  	case 0x00:
  		return "Connect";
  	case 0x01:
  		return "Disconnect";
  	case 0x02:
  		return "Put";
  	case 0x03:
  		return "Get";
  	case 0x04:
  		return "Reserved";
  	case 0x05:
  		return "SetPath";
  	case 0x06:
  		return "Action";
  	case 0x07:
  		return "Session";
  	case 0x7f:
  		return "Abort";
  	case 0x10:
  		return "Continue";
  	case 0x20:
  		return "Success";
  	case 0x21:
  		return "Created";
  	case 0x22:
  		return "Accepted";
  	case 0x23:
  		return "Non-authoritative information";
  	case 0x24:
  		return "No content";
  	case 0x25:
  		return "Reset content";
  	case 0x26:
  		return "Partial content";
  	case 0x30:
  		return "Multiple choices";
  	case 0x31:
  		return "Moved permanently";
  	case 0x32:
  		return "Moved temporarily";
  	case 0x33:
  		return "See other";
  	case 0x34:
  		return "Not modified";
  	case 0x35:
  		return "Use Proxy";
  	case 0x40:
  		return "Bad request";
  	case 0x41:
  		return "Unauthorized";
  	case 0x42:
  		return "Payment required";
  	case 0x43:
  		return "Forbidden";
  	case 0x44:
  		return "Not found";
  	case 0x45:
  		return "Method not allowed";
  	case 0x46:
  		return "Not acceptable";
  	case 0x47:
  		return "Proxy authentication required";
  	case 0x48:
  		return "Request timeout";
  	case 0x49:
  		return "Conflict";
  	case 0x4a:
  		return "Gone";
  	case 0x4b:
  		return "Length required";
  	case 0x4c:
  		return "Precondition failed";
  	case 0x4d:
  		return "Requested entity too large";
  	case 0x4e:
  		return "Requested URL too large";
  	case 0x4f:
  		return "Unsupported media type";
  	case 0x50:
  		return "Internal server error";
  	case 0x51:
  		return "Not implemented";
  	case 0x52:
  		return "Bad gateway";
  	case 0x53:
  		return "Service unavailable";
  	case 0x54:
  		return "Gateway timeout";
  	case 0x55:
  		return "HTTP version not supported";
  	case 0x60:
  		return "Database full";
  	case 0x61:
  		return "Database locked";
  	default:
  		return "Unknown";
  	}
  }
  
  static char *hi2str(uint8_t hi)
  {
  	switch (hi & 0x3f) {
  	case 0x00:
  		return "Count";
  	case 0x01:
  		return "Name";
  	case 0x02:
  		return "Type";
  	case 0x03:
  		return "Length";
  	case 0x04:
  		return "Time";
  	case 0x05:
  		return "Description";
  	case 0x06:
  		return "Target";
  	case 0x07:
  		return "HTTP";
  	case 0x08:
  		return "Body";
  	case 0x09:
  		return "End of Body";
  	case 0x0a:
  		return "Who";
  	case 0x0b:
  		return "Connection ID";
  	case 0x0c:
  		return "App. Parameters";
  	case 0x0d:
  		return "Auth. Challenge";
  	case 0x0e:
  		return "Auth. Response";
  	case 0x0f:
  		return "Creator ID";
  	case 0x10:
  		return "WAN UUID";
  	case 0x11:
  		return "Object Class";
  	case 0x12:
  		return "Session Parameters";
  	case 0x13:
  		return "Session Sequence Number";
  	case 0x14:
  		return "Action ID";
  	case 0x15:
  		return "DestName";
  	case 0x16:
  		return "Permission";
  	case 0x17:
  		return "Single Response Mode";
  	case 0x18:
  		return "Single Response Mode Parameters";
  	default:
  		return "Unknown";
  	}
  }
  
  static void parse_headers(int level, struct frame *frm)
  {
  	uint8_t hi, hv8;
  	uint16_t len;
  	uint32_t hv32;
  
  	while (frm->len > 0) {
  		hi = get_u8(frm);
  
  		p_indent(level, frm);
  
  		printf("%s (0x%02x)", hi2str(hi), hi);
  		switch (hi & 0xc0) {
  		case 0x00:	/* Unicode */
  			if (frm->len < 2) {
  				printf("\n");
  				return;
  			}
  
  			len = get_u16(frm) - 3;
  			printf(" = Unicode length %d\n", len);
  
  			if (frm->len < len)
  				return;
  
  			raw_ndump(level, frm, len);
  			frm->ptr += len;
  			frm->len -= len;
  			break;
  
  		case 0x40:	/* Byte sequence */
  			if (frm->len < 2) {
  				printf("\n");
  				return;
  			}
  
  			len = get_u16(frm) - 3;
  			printf(" = Sequence length %d\n", len);
  
  			if (frm->len < len)
  				return;
  
  			raw_ndump(level, frm, len);
  			frm->ptr += len;
  			frm->len -= len;
  			break;
  
  		case 0x80:	/* One byte */
  			if (frm->len < 1) {
  				printf("\n");
  				return;
  			}
  
  			hv8 = get_u8(frm);
  			printf(" = %d\n", hv8);
  			break;
  
  		case 0xc0:	/* Four bytes */
  			if (frm->len < 4) {
  				printf("\n");
  				return;
  			}
  
  			hv32 = get_u32(frm);
  			printf(" = %u\n", hv32);
  			break;
  		}
  	}
  }
  
  void obex_dump(int level, struct frame *frm)
  {
  	uint8_t last_opcode, opcode, status;
  	uint8_t version, flags, constants;
  	uint16_t length, pktlen;
  
  	frm = add_frame(frm);
  
  	while (frm->len > 2) {
  		opcode = get_u8(frm);
  		length = get_u16(frm);
  		status = opcode & 0x7f;
  
  		if ((int) frm->len < length - 3) {
  			frm->ptr -= 3;
  			frm->len += 3;
  			return;
  		}
  
  		p_indent(level, frm);
  
  		last_opcode = get_opcode(frm->handle, frm->dlci);
  
  		if (!(opcode & 0x70)) {
  			printf("OBEX: %s cmd(%c): len %d",
  					opcode2str(opcode),
  					opcode & 0x80 ? 'f' : 'c', length);
  			set_opcode(frm->handle, frm->dlci, opcode);
  		} else {
  			printf("OBEX: %s rsp(%c): status %x%02d len %d",
  					opcode2str(last_opcode),
  					opcode & 0x80 ? 'f' : 'c',
  					status >> 4, status & 0xf, length);
  			opcode = last_opcode;
  		}
  
  		if (get_status(frm->handle, frm->dlci) == 0x10)
  			printf(" (continue)");
  
  		set_status(frm->handle, frm->dlci, status);
  
  		if (frm->len == 0) {
  			printf("\n");
  			break;
  		}
  
  		switch (opcode & 0x7f) {
  		case 0x00:	/* Connect */
  			if (frm->len < 4) {
  				printf("\n");
  				return;
  			}
  
  			version = get_u8(frm);
  			flags   = get_u8(frm);
  			pktlen  = get_u16(frm);
  			printf(" version %d.%d flags %d mtu %d\n",
  				version >> 4, version & 0xf, flags, pktlen);
  			break;
  
  		case 0x05:	/* SetPath */
  			if (frm->len < 2) {
  				printf("\n");
  				return;
  			}
  
  			flags     = get_u8(frm);
  			constants = get_u8(frm);
  			printf(" flags %d constants %d\n", flags, constants);
  			break;
  
  		default:
  			printf("\n");
  			break;
  		}
  
  		if ((status & 0x70) && (parser.flags & DUMP_VERBOSE)) {
  			p_indent(level, frm);
  			printf("Status %x%02d = %s\n",
  					status >> 4, status & 0xf,
  							opcode2str(status));
  		}
  
  		parse_headers(level, frm);
  	}
  }
  