Skip to content
Snippets Groups Projects
Select Git revision
  • ee6ff2cc52df67191c06760f7e9c88e8e83862c1
  • master default protected
  • streebog
  • gost28147
  • master-updates
  • ed448
  • shake256
  • curve448
  • ecc-sqrt
  • gosthash94cp
  • cmac64
  • block16-refactor
  • siv-mode
  • cmac-layout
  • delete-des-compat
  • delete-rsa_blind
  • aes-struct-layout
  • release-3.4-fixes
  • struct-layout
  • attribute-deprecated
  • rename-data-symbols
  • nettle_3.5.1_release_20190627
  • nettle_3.5_release_20190626
  • nettle_3.5rc1
  • nettle_3.4.1_release_20181204
  • nettle_3.4.1rc1
  • nettle_3.4_release_20171119
  • nettle_3.4rc2
  • nettle_3.4rc1
  • nettle_3.3_release_20161001
  • nettle_3.2_release_20160128
  • nettle_3.1.1_release_20150424
  • nettle_3.1_release_20150407
  • nettle_3.1rc3
  • nettle_3.1rc2
  • nettle_3.1rc1
  • nettle_3.0_release_20140607
  • nettle_2.7.1_release_20130528
  • nettle_2.7_release_20130424
  • nettle_2.6_release_20130116
  • nettle_2.5_release_20120707
41 results

base64.h

Blame
  • Forked from Nettle / nettle
    Source project has a limited visibility.
    OBEX.pmod 13.37 KiB
    
    #pike __REAL_VERSION__
    
    //! The IrDA® Object Exchange Protocol.
    //! OBEX is a protocol for sending and receiving binary objects
    //! to mobile devices using transports such as IR and Bluetooth.
    
    //! A request opcode, for use with the @[client.do_request()] function.
    enum Request {
      REQ_CONNECT = 0x80,		//! Establish a new OBEX connection
      REQ_DISCONNECT = 0x81,	//! Terminate an OBEX connection
      REQ_PUT = 0x02,		//! Send an object to the mobile device
      REQ_GET = 0x03,		//! Receive an object from the mobile devuce
      REQ_SETPATH = 0x85,		//! Change the working directory
      REQ_SESSION = 0x87,		//! Manage a session
      REQ_ABORT = 0xff,		//! Abort the request currently being processed
    
      //! For @[REQ_PUT] and @[REQ_GET] requests, REQ_FINAL must be set
      //! for the request block containing the last portion of the headers.
      //! Other requests must be sent as a single block and have the REQ_FINAL
      //! bit encoded in their request opcode.
      REQ_FINAL = 0x80
    };
    
    //! An identifier for a request or response header
    enum HeaderIdentifier {
      HI_COUNT = 0xc0,	//! Number of objects to transfer (used by @[REQ_CONNECT])
      HI_NAME = 0x01,	//! Name of the object (string)
      HI_TYPE = 0x42,	//! Type of the object (IANA media type)
      HI_LENGTH = 0xc3,	//! Length of the object transferred, in octets
      HI_TIME = 0x44,	//! ISO 8601 timestamp (string)
      HI_DESCRIPTION = 0x05,//! Text description of the object
      HI_TARGET = 0x46,	//! Name of service that operation is targeted to
      HI_HTTP = 0x47,	//! Any HTTP 1.x header
      HI_BODY = 0x48,	//! A chunk of the object body
      HI_ENDOFBODY = 0x49,	//! The final chunk of the object body
      HI_WHO = 0x4a,	//! Identifies the OBEX application (string)
      HI_CONNID = 0xcb,	//! An identifier used for OBEX connection multiplexing
      HI_APPPARAM = 0x4c,	//! Extended application request & response information
      HI_AUTHCHALL = 0x4d,	//! Authentication digest-challenge
      HI_AUTHRESP = 0x4e,	//! Authentication digest-response
      HI_CREATORID = 0xcf,	//! Indicates the creator of an object
      HI_WANUUID = 0x50,	//! Uniquely identifies the OBEX server
      HI_OBJCLASS = 0x51,	//! OBEX object class of object
      HI_SESSPARAM = 0x52,	//! Parameters used in session commands/responses
      HI_SESSSEQNR = 0x93,	//! Sequence number used in each OBEX packet for reliability
    };
    
    //! A flag for the @[REQ_SETPATH] command indicating that the
    //! parent directory should be selected
    final constant SETPATH_BACKUP = 1;
    
    //! A flag for the @[REQ_SETPATH] command indicating that the
    //! selected directory should not be created if it doesn't exist
    final constant SETPATH_NOCREATE = 2;
    
    //! A set of request or response headers.  Each HI can be associated
    //! with either a single value (int or string, depending on the HI in
    //! question) or an array with multiple such values.
    typedef mapping(HeaderIdentifier:string|int|array) Headers;
    
    
    //! Serialize a set of headers to wire format
    //!
    //! @seealso
    //!   @[split_headers()]
    //!
    string encode_headers(Headers h)
    {
      string r0 = "", r = "";
      object e = Charset.encoder("utf16-be");
      foreach(h; HeaderIdentifier hi; string|int|array va)
        foreach(arrayp(va)? va:({va}), string|int v) {
          string vv;
          switch(hi>>6) {
          case 0:
    	v = e->clear()->feed(v)->drain()+"\0\0";
          case 1:
    	vv = sprintf("%c%2c%s", hi, sizeof(v)+3, v);
    	break;
          case 2:
    	vv = sprintf("%c%c", hi, v);
    	break;
          case 3:
    	vv = sprintf("%c%4c", hi, v);
    	break;
          }
          if(hi == HI_SESSPARAM)
    	r0 += vv;
          else
    	r += vv;
        }
      return r0+r;
    }
    
    //! Deserialize a set of headers from wire format
    //!
    Headers decode_headers(string h)
    {
      Headers r = ([]);
      object d = Charset.decoder("utf16-be");
      while(sizeof(h)) {
        HeaderIdentifier hi = h[0];
        string|int v = 0;
        switch(hi>>6) {
        case 0:
        case 1:
          if(sizeof(h)>=3) {
    	int l;
    	sscanf(h[1..2], "%2c", l);
    	v = h[3..2+l];
    	h = h[3+l..];
          } else h=v="";
          if(hi < 0x40) {
    	if(has_suffix(v, "\0\0"))
    	  v = v[..<2];
    	v = d->clear()->feed(v)->drain();
          }
          break;
        case 2:
          if(sizeof(h)>=2)
    	v = h[1];
          h = h[2..];
          break;
        case 3:
          if(sizeof(h)>=5)
    	sscanf(h[1..4], "%4c", v);
          h = h[5..];
          break;
        }
        if(!has_index(r, hi))
          r[hi] = v;
        else if(arrayp(r[hi]))
          r[hi] += ({ v });
        else
          r[hi] = ({  r[hi], v });
      }
      return r;
    }
    
    //! Given a set of headers in wire format, divide them into
    //! portions of no more than @[chunklen] octets each (if possible).
    //! No individual header definition will be split into two portions.
    //!
    array(string) split_headers(string h, int chunklen)
    {
      string acc = "";
      array(string) r = ({});
    
      while(sizeof(h)) {
        int l = 0;
        switch(h[0]>>6) {
        case 0:
        case 1:
          if(sizeof(h)>=3)
    	sscanf(h[1..2], "%2c", l);
          if(l<3) l = 3;
          break;
        case 2:
          l = 2;
          break;
        case 3:
          l = 5;
          break;
        }
        if(l>sizeof(h))
          l = sizeof(h);
        if(sizeof(acc) && sizeof(acc)+l > chunklen) {
          r += ({ acc });
          acc = "";
        }
        acc += h[..l-1];
        h = h[l..];
      }
    
      return r + (sizeof(acc) || !sizeof(r)? ({ acc }) : ({ }));
    }
    
    
    //! An OBEX client
    //!
    //! @seealso
    //!   @[ATclient]
    //!
    class Client
    {
      protected Stdio.Stream con;
      protected int connected = 0;
    
      protected constant obex_version = 0x10;
      protected constant connect_flags = 0;
      protected constant default_max_pkt_length = 65535;
    
      protected int server_version, server_connect_flags, max_pkt_length = 255;
    
    
      //! Perform a request/response exchange with the server.
      //! No interpretation is preformed of either the request or
      //! response data, they are just passed literally.
      //!
      //! @param r
      //!   Request opcode
      //! @param data
      //!   Raw request data
      //!
      //! @returns
      //!   An array with the response information
      //!
      //!   @array
      //!     @elem int returncode
      //!	    An HTTP response code
      //!	  @elem string data
      //!	    Response data, if any
      //!   @endarray
      //!
      //! @seealso
      //!   @[do_request()]
      //!
      array(int|string) low_do_request(Request r, string data)
      {
        if(3+sizeof(data) > max_pkt_length)
          return ({ 400, "" });
    
    #ifdef OBEX_DEBUG
        werror("OBEX SENDING %O\n", sprintf("%c%2c%s", r, 3+sizeof(data), data));
    #endif
        con->write(sprintf("%c%2c%s", r, 3+sizeof(data), data));
        [int rc, int rlen] = array_sscanf(con->read(3), "%c%2c");
    #ifdef OBEX_DEBUG
        werror("OBEX RESPONSE: %02x (+ %d bytes)\n", rc, rlen-3);
    #endif
        string rdata = (rlen>3? con->read(rlen-3):"");
    #ifdef OBEX_DEBUG
        if(sizeof(rdata))
          werror("OBEX EXTRA RESPONSE DATA: %O\n", rdata);
    #endif
        if(!(rc & 0x80))
          error("OBEX got response code without final bit!\n");
        rc &= 0x7f;
        return ({ (rc>>4)*100+(rc&15), rdata });
      }
    
      //! Perform a request/response exchange with the server,
      //! including processing of headers and request splitting.
      //!
      //! @param r
      //!   Request opcode
      //! @param headers
      //!   Request headers
      //! @param extra_req
      //!   Any request data that should appear before the headers,
      //!   but after the opcode
      //!
      //! @returns
      //!   An array with the response information
      //!
      //!   @array
      //!     @elem int returncode
      //!	    An HTTP response code
      //!	  @elem Headers headers
      //!	    Response headers
      //!   @endarray
      //!
      //! @seealso
      //!   @[low_do_request()], @[do_abort()], @[do_put()], @[do_get()],
      //!   @[do_setpath()], @[do_session()]
      //!
      array(int|Headers) do_request(Request r, Headers|void headers,
    				string|void extra_req)
      {
        if(!connected)
          return ({ 503, ([]) });
    
        string hh = encode_headers(headers||([]));
    
        if(!extra_req)
          extra_req = "";
    
        if(sizeof(extra_req)+sizeof(hh)+3 > max_pkt_length) {
          array(string) hh_split = split_headers(hh, max_pkt_length-3-
    					     sizeof(extra_req));
          hh = hh_split[-1];
          foreach(hh_split[..<1], string h0) {
    	[int rc0, string rdata0] = low_do_request(r&~REQ_FINAL, extra_req+h0);
    	if(rc0 != 100)
    	  return ({ rc0, (rc0==501? ([]): decode_headers(rdata0)) });
          }
        }
    
        [int rc, string rdata] = low_do_request(r, extra_req+hh);
        return ({ rc, (rc==501? ([]): decode_headers(rdata)) });
      }
    
      //! Perform a @[REQ_ABORT] request.
      //!
      //! @seealso
      //!   @[do_request()]
      //!
      array(int|Headers) do_abort(Headers|void headers)
      {
        [int rc, Headers h] = do_request(REQ_ABORT, headers);
        if(rc != 200)
          disconnect();
        return ({ rc, h });
      }
    
      //! Perform a @[REQ_PUT] request.
      //!
      //! @param data
      //!   Body data to send, or a stream to read the data from
      //! @param extra_headers
      //!   Any additional headers to send (@[HI_LENGTH] and @[HI_BODY]
      //!   are generated by this function)
      //!
      //! @seealso
      //!   @[do_get()], @[do_request()]
      //!
      array(int|Headers) do_put(string|Stdio.Stream data,
    			    Headers|void extra_headers)
      {
        string sdata = "";
    
        if(stringp(data))
          sdata = data;
        else {
          string r;
          while(sizeof(r = data->read(65536)))
    	sdata += r;
        }
    
        [int rc, Headers h] = do_request(REQ_PUT, (extra_headers||([]))|
    				     ([HI_LENGTH:sizeof(sdata)]));
        while(rc == 100) {
          Headers h2;
          if(sizeof(sdata)+6 > max_pkt_length) {
    	[rc, h2] = do_request(REQ_PUT, ([HI_BODY:sdata[..max_pkt_length-7]]));
    	sdata = sdata[max_pkt_length-6..];
          } else
    	[rc, h2] = do_request(REQ_PUT|REQ_FINAL, ([HI_ENDOFBODY:sdata]));
          h |= h2;
        }
    
        return ({ rc, h });
      }
    
      //! Perform a @[REQ_GET] request.
      //!
      //! @param data
      //!   A stream to write the body data to
      //! @param headers
      //!   Headers for the request
      //!
      //! @returns
      //!   See @[do_request()].  The Headers do not contain any @[HI_BODY]
      //!   headers, they are written to the @[data] stream.
      //!
      //! @seealso
      //!   @[do_put()], @[do_request()]
      //!
      array(int|Headers) do_get(Stdio.Stream data,
    			    Headers|void headers)
      {
        [int rc, Headers h] = do_request(REQ_GET|REQ_FINAL, headers||([]));
    
        for(;;) {
          if(rc == 100 || rc == 200) {
    	data->write(((h[HI_BODY]||({}))+(h[HI_ENDOFBODY]||({})))*"");
    	m_delete(h, HI_BODY);
    	m_delete(h, HI_ENDOFBODY);
          }
          if(rc != 100)
    	break;
          [rc, Headers h2] = do_request(REQ_GET|REQ_FINAL);
          h |= h2;
        }
    
        return ({ rc, h });
      }
    
      //! Perform a @[REQ_SETPATH] request.
      //!
      //! @param path
      //!   The directory to set as current working directory
      //!   @string
      //!     @value "/"
      //!       Go to the root directory
      //!     @value ".."
      //!       Go to the parent directory
      //!   @endstring
      //! @param flags
      //!   Logical or of zero or more of @[SETPATH_BACKUP] and @[SETPATH_NOCREATE]
      //! @param extra_headers
      //!   Any additional request headers (the @[HI_NAME] header is generated
      //!   by this function)
      //!
      //! @seealso
      //!   @[do_request()]
      //!
      array(int|Headers) do_setpath(string path, int|void flags,
    				Headers|void extra_headers)
      {
        if(path == "/")
          path = "";
        else if(path = "..") {
          path = "";
          flags |= SETPATH_BACKUP;
        }
        return do_request(REQ_SETPATH, (extra_headers||([]))|([HI_NAME:path]),
    		      sprintf("%c%c", flags, 0));
      }
    
      //! Perform a @[REQ_SESSION] request.
      //!
      //! @seealso
      //!   @[do_request()]
      //!
      array(int|Headers) do_session(Headers|void headers)
      {
        return do_request(REQ_SESSION, headers);
      }
    
      //! Establish a new connection using the @[REQ_CONNECT] opcode to
      //! negotiate transfer parameters
      //!
      //! @returns
      //!   If the connection succeeds, 1 is returned.  Otherwise, 0 is returned.
      //!
      int(0..1) connect()
      {
        if(connected)
          return 1;
    
        [int rc, string cdata] =
          low_do_request(REQ_CONNECT,
    		     sprintf("%c%c%2c", obex_version, connect_flags,
    			     default_max_pkt_length));
    
        if(rc != 200)
          return 0;
    
        sscanf(cdata, "%c%c%2c", server_version, server_connect_flags,
    	   max_pkt_length);
    
        connected = 1;
    
    #ifdef OBEX_DEBUG
        werror("OBEX connected, server is version %d.%d, max packet length = %d\n",
    	   server_version>>4, server_version&15, max_pkt_length);
    #endif
    
        return 1;
      }
    
      //! Terminate a connection using the @[REQ_DISCONNECT] opcode
      //!
      //! @returns
      //!   If the disconnection succeeds, 1 is returned.  Otherwise, 0 is returned.
      //!
      int(0..1) disconnect()
      {
        if(!connected)
          return 1;
    
        [int rc, string ddata] =
          low_do_request(REQ_DISCONNECT, "");
    
        if(rc != 200)
          return 0;
    
        connected = 0;
    
    #ifdef OBEX_DEBUG
        werror("OBEX disconnected.\n");
    #endif
      }
    
      protected void destroy()
      {
        if(connected)
          disconnect();
      }
    
      //! Initialize the client by establishing a connection to the
      //! server at the other end of the provided transport stream
      //!
      //! @param _con
      //!   A stream for writing requests and reading back responses.
      //!   Typically this is some kind of serial port.
      //!
      protected void create(Stdio.Stream _con)
      {
        con = _con;
        if(!connect())
          error("Failed to establish OBEX connection\n");
      }
    }
    
    
    //! An extension of the @[client] which uses the AT*EOBEX modem command
    //! to enter OBEX mode.  Use together with Sony Ericsson data cables.
    //!
    class ATClient
    {
      inherit Client;
    
      int(0..1) connect()
      {
        if(connected)
          return 1;
    
        con->write("\rAT*EOBEX\r");
        string line, resp = "AT";
        for(;;) {
          int cr;
          resp += con->read(1);
          if((cr = search(resp, "\r\n"))>=0) {
    	line = resp[..cr-1];
    	if(sizeof(line) && line[..1] != "AT" && line != "OK")
    	  break;
    	resp = resp[cr+2..];
          }
        }
    
        if(line != "CONNECT")
          return 0;
    
        return ::connect();
      }
    }