
            Description of the API used by up2date version 2

I. Major changes:

The most prominent change (which complicated the whole picture a little bit)
is the notion of channel. A channel is an abstraction of what we previously
called a "distribution". It is a logical grouping of packages known to
correctly run on a system. This means no dependency problems should exit a
channel; i.e. channels should be self-contained with respect to RPM
dependencies.

A channel has a "base architecture" as an implicit feature. One should not be
able to put a package in a channel with an incompatible architecture.

XMLRPC is no longer the only way the client and the server talk to each other.
Because the mechanism to pack XMLRPC involves POST requests, the result of an
XMLRPC function call is impossible to be cached on regular HTTP
proxies/caches. For this reason, some of the bandwidth-hungry operations
(fetching the list of packages, a package header, a binary or a source
package) have been converted to use GET requests, and have been tested to work
with HTTP proxies/caches (we tested Squid and Apache).

A fault for a call is no longer packed as an HTTP 200 response for GET
requests. Instead we pass back HTTP_NOT_FOUND and (in certain situations)
HTTP_UNAUTHORIZED. The XMLRPC Fault is packed in the header of the response,
instead of the body.

We ran limited testing of running the up2date client over HTTP/1.1. While the
results seemed to be correct, a more thorough analysis of the implications and
complications HTTP/1.1 would bring is welcome.

We have tested the old clients to run against the current code; this implies
that the old XMLRPC calls run exactly as before. Some complications occur
though when channels kick in, since they completely ignore the channel
concept.


II. What an up2date client is supposed to do (the work-flow of the protocol):

1. the client issues an XMLRPC request for up2date.login, passing its system
  ID. A successful lookup of the server will return a dictionary of items
  which are supposed to be passed back to the server (in the header of the GET
  request) when GET functions are called. The dictionary typically contains
  the following keys:
    'X-RHN-Client-Version':     set to '2'
    'X-RHN-Auth-Expire-Offset': the string representation of a float, the
          number of seconds until the request expires
    'X-RHN-Auth-User-Id':       the username (string)
    'X-RHN-Server-Id':          the server ID (an integer)
    'X-RHN-Auth':               a signature generated by the server, which
        will be verified to establish the client authenticity
    'X-RHN-Auth-Server-Time':   the time (string representation of a float
        value, the number of seconds from the epoch, Jan 1, 1970) on the 
        server side) when this authentication token was generated. Clients
        could use this value to detect clock skews and to figure out when the
        authentication token will expire (and to renew it). The expiration
        time is the server time plus the 'X-RHN-Auth-Expire-Offset' value.
    'X-RHN-Auth-Channels':      a list of channels this server is subscribed
        to. Each entry in the list is in the form 
        [channel_name, channel_version], both strings.
        This key is for the client use only and is not used by the server to
        compute the signature, so it may be excluded from the dictionary the
        client sends back to the server

2. the client creates an rhnHTTPlib.Server, using the server's URL and the
  dictionary passed as 'headers'. Underneath, rhnHTTPlib will send all the
  (key, value) pairs as header fields for the GET request. An array value is
  passed as a multi-valued header.

3. using the server object created on step 2, the client can issue the
   following functions:
    - listPackages(channelName, channelVersion). The result is an
      XMLRPC-encoded list of entries. Each entry is (currently) a 7-item list:
      [packageName, packageVersion, packageRelease, packageEpoch, packageArch,
          packageSize, channelName]
      After retrieving the string returned by this function, one should 
      XMLRPC decode it in order to obtain the list.
    - getPackageHeader(channelName, headerName),
      where headerName closely mimics the way RPM filenames are generated,
      replacing the trailing .rpm with .hdr. For example, to retrieve the
      package returned by a listPackages call which reads:
        ['kernel', '2.4.3', '12', '', 'i686', '13441234',
         'redhat-linux-i386-7.1']
      one should issue:
        getPackageHeader('redhat-linux-i386-7.1', 'kernel-2.4.3-12.i686.hdr')
      The return value of this function is binary data, ready to be fed to the
      RPM library to obtain a header object.
    - getPackage(channelName, packageName), 
      where packageName closely mimics the way RPM filenames are generated.
      For example, to retrieve the package returned by a listPackages call 
      which reads:
        ['kernel', '2.4.3', '12', '', 'i686', '13441234',
         'redhat-linux-i386-7.1']
      one should issue:
        getPackage('redhat-linux-i386-7.1', 'kernel-2.4.3-12.i686.rpm').
      The return value of this function is binary data, ready to be saved in a
      file and considered a valid RPM package.
    - getPackageSource(channelName, packageSourceName),
      where packageName closely mimics the way RPM filenames are generated.
      For example, to retrieve the package returned by a listPackages call 
      which reads:
        ['kernel', '2.4.3', '12', '', 'i686', '13441234',
         'redhat-linux-i386-7.1']
      one should issue:
        getPackageSource('redhat-linux-i386-7.1', 'kernel-2.4.3-12.src.rpm')
      The return value of this function is binary data, ready to be saved in a
      file and considered a valid RPM package.
    
All these functions return a tuple (size, fd) if the call was successful
(i.e. if the HTTP result code is 200); otherwise, rhnHTTPlib raises a
cgiwrap.ProtocolError exception. The errcode, errmsg and headers fields of the
exception object contain the correspondent values of the result.

One should be careful when reading from the file-descriptor-like object
returned by the GET functions. HTTP/1.1 uses persistent connections, so one
should try to read exactly the size returned by the function. Failure to do so
may result in corruption of data.

