/home/dko/projects/mobilec/trunk/src/mtp_http.c

Go to the documentation of this file.
00001 /*[
00002  * Copyright (c) 2007 Integration Engineering Laboratory
00003                       University of California, Davis
00004  *
00005  * Permission to use, copy, and distribute this software and its
00006  * documentation for any purpose with or without fee is hereby granted,
00007  * provided that the above copyright notice appear in all copies and
00008  * that both that copyright notice and this permission notice appear
00009  * in supporting documentation.
00010  *
00011  * Permission to modify the software is granted, but not the right to
00012  * distribute the complete modified source code.  Modifications are to
00013  * be distributed as patches to the released version.  Permission to
00014  * distribute binaries produced by compiling modified sources is granted,
00015  * provided you
00016  *   1. distribute the corresponding source modifications from the
00017  *    released version in the form of a patch file along with the binaries,
00018  *   2. add special version identification to distinguish your version
00019  *    in addition to the base release version number,
00020  *   3. provide your name and address as the primary contact for the
00021  *    support of your modified version, and
00022  *   4. retain our contact information in regard to use of the base
00023  *    software.
00024  * Permission to distribute the released version of the source code along
00025  * with corresponding source modifications in the form of a patch file is
00026  * granted with same provisions 2 through 4 for binary distributions.
00027  *
00028  * This software is provided "as is" without express or implied warranty
00029  * to the extent permitted by applicable law.
00030 ]*/
00031 
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #ifndef _WIN32
00036 #include "config.h"
00037 #else
00038 #include "winconfig.h"
00039 #endif
00040 #include "include/connection.h"
00041 #include "include/mtp_http.h"
00042 #include "include/macros.h"
00043 #include "include/mc_error.h"
00044 #include "include/message.h"
00045 #include "include/dynstring.h"
00046 
00047 #define SOCKET_INPUT_SIZE 4096
00048 int
00049 mtp_http_Destroy(mtp_http_p http)
00050 {
00051   int i;
00052 #define SAFE_FREE(elem) \
00053   if(elem) \
00054     free(elem)
00055 
00056   SAFE_FREE(http->http_version);
00057   SAFE_FREE(http->host);
00058   SAFE_FREE(http->return_code);
00059   SAFE_FREE(http->target);
00060   SAFE_FREE(http->date);
00061   SAFE_FREE(http->server);
00062   SAFE_FREE(http->accept_ranges);
00063   SAFE_FREE(http->content_length);
00064   SAFE_FREE(http->connection);
00065   SAFE_FREE(http->content_type);
00066   SAFE_FREE(http->user_agent);
00067   if(http->content != NULL) {
00068     for(i = 0; i < http->message_parts; i++) {
00069       SAFE_FREE(http->content[i].content_type);
00070       SAFE_FREE(http->content[i].data);
00071     }
00072   }
00073   SAFE_FREE(http->content);
00074   SAFE_FREE(http->boundary);
00075   SAFE_FREE(http);
00076 #undef SAFE_FREE
00077   return 0;
00078 }
00079 
00080   mtp_http_p
00081 mtp_http_New(void)
00082 {
00083   mtp_http_p http;
00084   http = (mtp_http_p)malloc(sizeof(mtp_http_t));
00085   CHECK_NULL(http, exit(0););
00086   memset(http, 0, sizeof(mtp_http_t));
00087   http->content = NULL;
00088   return http;
00089 }
00090 
00091 int
00092 mtp_http_InitializeFromConnection
00093 (
00094  mtp_http_p http,
00095  connection_p connection
00096  )
00097 {
00098   int i=1;
00099   int n;
00100   char *message_string;
00101   char *buffer;
00102   int message_size = 0;
00103 
00104   buffer = (char*) malloc(sizeof(char) * (SOCKET_INPUT_SIZE + 1));
00105   CHECK_NULL(buffer, exit(0););
00106   message_string = (char*) malloc(sizeof(char) * (SOCKET_INPUT_SIZE + 1));
00107   CHECK_NULL(message_string, exit(0););
00108   message_string[0] = '\0';
00109   buffer[0] = '\0';
00110 
00111   while(1){
00112 #ifndef _WIN32
00113     n = recvfrom(connection->clientfd,
00114         (void *) buffer,
00115         (size_t) sizeof(char)*SOCKET_INPUT_SIZE,
00116         0,
00117         (struct sockaddr *) 0,
00118         (socklen_t *) 0);
00119 #else
00120     n = recvfrom(connection->clientfd,
00121         (void *) buffer,
00122         (size_t) sizeof(char)*SOCKET_INPUT_SIZE,
00123         0,
00124         (struct sockaddr *) 0,
00125         0);
00126 #endif
00127     if (n < 0) {
00128       free(buffer);
00129       return MC_ERR_CONNECT;
00130     } 
00131     else if (n == 0) {
00132       free(buffer);
00133       break;
00134     } else {
00135       message_size += n;
00136       buffer[n] = '\0';
00137       i++;
00138       strcat(message_string, buffer);
00139       message_string = realloc
00140         (
00141          message_string, 
00142          sizeof(char) * (SOCKET_INPUT_SIZE+1) * i
00143         );
00144       CHECK_NULL(message_string, exit(0););
00145       buffer[0] = '\0';
00146       if (!strcmp
00147           (
00148            message_string + message_size - 4,
00149            "\r\n\r\n"
00150           )
00151          )
00152         break;
00153     }
00154   }
00155   
00156   if(mtp_http_Parse(http, message_string)) {
00157     /* Reply with an HTTP error code */
00158     buffer = malloc
00159       (
00160        sizeof(char) * 
00161        (
00162         strlen
00163         (
00164          "HTTP/1.0 503 Service Unavailable\r\nConnection: Close\r\n\r\nMobile C"
00165         )+1
00166        )
00167       );
00168     strcpy
00169       (
00170        buffer,
00171        "HTTP/1.0 503 Service Unavailable\r\nConnection: Close\r\n\r\nMobile C"
00172       );
00173     send
00174       (
00175        connection->clientfd,
00176        (void*)buffer,
00177        sizeof(char)*(strlen(buffer)),
00178        0
00179       );
00180     free(message_string);
00181     return ERR;
00182   } else {
00183     free(message_string);
00184     return 0;
00185   }
00186 }
00187 
00188 const char* http_GetExpression(const char* string, char** expr)
00189 {
00190   int i;
00191   int j;
00192   int success_flag = 0;
00193   const char* next_expr_ptr;
00194   /* Check to see if we are at the end of the HTTP header */
00195   if( 
00196       (string[0] == '\n') ||
00197       (string[0] == '\r' && string[1] == '\n')
00198     ) 
00199   {
00200     for(i = 0; string[i] == '\n' || string[i] == '\r'; i++);
00201     *expr = NULL;
00202     return string+i;
00203   }
00204   /* FIXME */
00205   for(i = 0;string[i] != '\0';i++) {
00206     if (
00207         (
00208          (string[i] == '\r') &&
00209          (string[i+1] == '\n') &&
00210          (string[i+2] != '\t') &&
00211          (string[i+2] != ' ')
00212         ) 
00213         ||
00214         (
00215          string[i] == '\n' && 
00216          string[i+1] != '\t' &&
00217          string[i+2] != ' '
00218         )
00219        )
00220     {
00221       success_flag = 1;
00222       break;
00223     } 
00224   }
00225   if(success_flag)
00226   {
00227     *expr = (char*)malloc
00228       (
00229        sizeof(char) * (i+1)
00230       );
00231     for(j = 0; j < i; j++) {
00232       (*expr)[j] = string[j];
00233     }
00234     (*expr)[j] = '\0';
00235     next_expr_ptr = &(string[i]);
00236     if(next_expr_ptr[0] == '\r' && next_expr_ptr[1] == '\n') {
00237       next_expr_ptr += 2;
00238     } else if (next_expr_ptr[0] == '\n') {
00239       next_expr_ptr++;
00240     }
00241     return next_expr_ptr;
00242   } else {
00243     return NULL;
00244   }
00245 }
00246       
00247 int http_ParseExpression(
00248     const char* expression_string,
00249     char** name,
00250     char** value
00251     )
00252 {
00253   int i=0;
00254   const char* tmp;
00255   const char* charptr;
00256   if(expression_string == NULL) {
00257     *name = NULL;
00258     *value = NULL;
00259     return MC_ERR_PARSE;
00260   }
00261   tmp = expression_string;
00262   if (tmp == NULL || (!strncmp(tmp, "\r\n", 2)) || (!strncmp(tmp, "\n", 1))) {
00263     *name = NULL;
00264     *value = NULL;
00265     return MC_ERR_PARSE;
00266   }
00267   for(; *tmp!=':' && *tmp!='\0'; tmp++)
00268     i++;
00269   if(*tmp == '\0') {
00270     *name = NULL;
00271     *value = NULL;
00272     return MC_ERR_PARSE;
00273   }
00274   *name = (char*)malloc
00275     (
00276      sizeof(char) * (i+1)
00277     );
00278   CHECK_NULL(*name, exit(0););
00279   charptr = expression_string;
00280   i=0;
00281   while(charptr != tmp) {
00282     (*name)[i] = *charptr;
00283     i++;
00284     charptr++;
00285   }
00286   (*name)[i] = '\0';
00287 
00288   tmp++;
00289   while
00290     (
00291      (*tmp == ' ') ||
00292      (*tmp == '\t')
00293     )
00294       tmp++;
00295 
00296   *value = (char*)malloc
00297     (
00298      sizeof(char) * 
00299      (strlen(tmp) + 1)
00300     );
00301   CHECK_NULL(*value, exit(0););
00302   strcpy(*value, tmp);
00303   return MC_SUCCESS;
00304 }
00305 
00306 int
00307 mtp_http_Parse(struct mtp_http_s* http, const char* string)
00308 {
00309   const char* line = NULL;
00310   char* expr = NULL;
00311   char* name = NULL;
00312   char* value = NULL;
00313   char* tmp;
00314   char* tmp2;
00315   int i;
00316 
00317   int err_code = 0;
00318 
00319   line = string;
00320   line = http_ParseHeader
00321     (
00322      http,
00323      line
00324     );
00325   do
00326   {
00327     line = http_GetExpression
00328       (
00329        line,
00330        &expr
00331       );
00332 
00333     err_code = http_ParseExpression
00334       (
00335        expr,
00336        &name,
00337        &value
00338       );
00339     if
00340       (
00341        (name == NULL) ||
00342        (value == NULL)
00343       )
00344       {
00345         if (expr != NULL) {
00346           free(expr);
00347         }
00348         break;
00349       }
00350 #define HTTP_PARSE_EXPR( parse_name, struct_name ) \
00351     if ( !strcmp(name, parse_name) ) { \
00352       http->struct_name = (char*)malloc \
00353       ( \
00354         sizeof(char) * \
00355         (strlen(value)+1) \
00356       ); \
00357       strcpy(http->struct_name, value); \
00358     } else
00359 
00360     HTTP_PARSE_EXPR( "Host", host )
00361     HTTP_PARSE_EXPR( "Date", date )
00362     HTTP_PARSE_EXPR( "Server", server )
00363     HTTP_PARSE_EXPR( "Accept-Ranges", accept_ranges )
00364     HTTP_PARSE_EXPR( "Content-Length", content_length)
00365     HTTP_PARSE_EXPR( "Connection", connection )
00366     HTTP_PARSE_EXPR( "Content-Type", content_type)
00367     HTTP_PARSE_EXPR( "User-Agent", user_agent)
00368     HTTP_PARSE_EXPR( "Cache-Control", cache_control)
00369     HTTP_PARSE_EXPR( "MIME-Version", mime_version)
00370     {
00371       fprintf(stderr, "Warning: Unknown http name: %s. %s:%d\n",
00372           name, __FILE__, __LINE__);
00373     }
00374 #undef HTTP_PARSE_EXPR
00375 
00376 #define SAFE_FREE( object ) \
00377     if(object) free(object); \
00378     object = NULL
00379 
00380     SAFE_FREE(expr);
00381     SAFE_FREE(name);
00382     SAFE_FREE(value);
00383 #undef SAFE_FREE
00384      
00385   } while(line != NULL && err_code == MC_SUCCESS);
00386   /* If the content type is multipart/mixed, then we need to
00387    * figure out how many parts there are. */
00388   if( 
00389       !strncmp(
00390         http->content_type,
00391         "multipart-mixed",
00392         strlen("multipart-mixed")
00393         ) 
00394     ) 
00395   {
00396     tmp = strstr(http->content_type, "boundary=");
00397     tmp += strlen("boundary=.");
00398     tmp2 = strchr(tmp, '\"');
00399     http->boundary = (char*)malloc(sizeof(char) * (tmp2 - tmp + 1));
00400     for (i = 0; tmp != tmp2; i++, tmp++) {
00401       http->boundary[i] = *tmp;
00402     }
00403     http->boundary[i] = '\0';
00404 
00405     /* Count the number of message parts in the message */
00406     tmp = (char*)line;
00407     http->message_parts = 0;
00408     while(tmp = strstr(tmp, http->boundary)) {
00409       http->message_parts++;
00410       tmp++;
00411     }
00412     http->message_parts--;
00413   } else {
00414     http->boundary = NULL;
00415     http->message_parts = 1;
00416   }
00417 
00418   if (http->message_parts == 1) { 
00419     http->content = (struct mtp_http_content_s*)malloc(sizeof(struct mtp_http_content_s));
00420     /* Copy rest of contents into the data member */
00421     if (line != NULL) {
00422       http->content->data = (void*)malloc
00423         (
00424          sizeof(char) * 
00425          (strlen(line)+1)
00426         );
00427       strcpy((char*)http->content->data, line);
00428       http->content->content_type = strdup(http->content_type);
00429     }
00430   } else {
00431     http->content = (struct mtp_http_content_s*)malloc(
00432         sizeof(struct mtp_http_content_s) * http->message_parts );
00433     memset(http->content, 0, sizeof(struct mtp_http_content_s) * http->message_parts);
00434     /* Find the boundary */
00435     line = strstr(line, http->boundary);
00436     line += strlen(http->boundary);
00437     line = strchr(line, '\n');
00438     line++;
00439     for(i = 0; i < http->message_parts; i++) {
00440       /* For each part, we must:
00441        * 1. Find the boundary
00442        * 2. Parse the attributes, until we
00443        * 3. Find the empty line
00444        * 4. Copy the data up to the next boundary */
00445       
00446       /* Find the end boundary */
00447       tmp = strstr(line + strlen(http->boundary), http->boundary);
00448       /* Parse the attributes */
00449       do{
00450         /* FIXME */
00451 
00452         line = http_GetExpression
00453           (
00454            line,
00455            &expr
00456           );
00457 
00458         err_code = http_ParseExpression
00459           (
00460            expr,
00461            &name,
00462            &value
00463           );
00464         if
00465           (
00466            (name == NULL) ||
00467            (value == NULL)
00468           )
00469           {
00470             if (expr != NULL) {
00471               free(expr);
00472             }
00473             break;
00474           }
00475         if (!strcmp(name, "Content-Type")) {
00476           http->content[i].content_type = (char*)malloc(
00477               sizeof(char) * (strlen(value)+1));
00478           strcpy(http->content[i].content_type, value);
00479         }
00480 
00481         /* Clean up memory */
00482         if (expr != NULL) {
00483           free(expr);
00484           expr = NULL;
00485         }
00486         if (name != NULL) {
00487           free(name);
00488           name = NULL;
00489         }
00490         if (value != NULL) {
00491           free(value);
00492           value = NULL;
00493         }
00494       } while(line != NULL && err_code == MC_SUCCESS);
00495       /* Copy the data */
00496       http->content[i].data = (void*)malloc(tmp-line+sizeof(char));
00497       memcpy(http->content[i].data, line, tmp-line);
00498       ((char*)http->content[i].data)[tmp-line] = '\0';
00499       /* Move 'line' to the next boundary */
00500       line = tmp + strlen(http->boundary);
00501       line = strchr(line, '\n');
00502       line++;
00503     }
00504   }
00505   if (
00506       (http->http_performative == HTTP_POST) ||
00507       (http->http_performative == HTTP_PUT)
00508      )
00509     return 0;
00510   else
00511     return 1;
00512 }
00513 
00514 const char* http_ParseHeader(
00515     mtp_http_p http,
00516     const char* string )
00517 {
00518   const char* cur;
00519   char* token;
00520   char* tmp = NULL;
00521   char* target;
00522 
00523   cur = string;
00524   cur = http_GetToken(cur, &token);
00525   if (token == NULL) {
00526     return NULL;
00527   }
00528   if (!strcmp(token, "GET")) {
00529     http->http_performative = HTTP_GET;
00530     cur = http_GetToken(cur, &tmp);
00531     /* We don't support 'get' yet */
00532     if(tmp) free(tmp);
00533   } else if(!strcmp(token, "HEAD")) {
00534     /* Don't support this yet */
00535     http->http_performative = HTTP_HEAD;
00536   } else if(!strcmp(token, "POST")) {
00537     http->http_performative = HTTP_POST;
00538     cur = http_GetToken(cur, &tmp);
00539     if(tmp != NULL) {
00540       if(!strncmp(tmp, "http://",7)) {
00541         target = strchr(tmp+7, (int)'/');
00542       } else {
00543         target = strchr(tmp, (int)'/');
00544       }
00545       if (target == NULL)
00546         target = tmp;
00547       http->target = (char*) malloc(sizeof(char) * (strlen(target)+1));
00548       strcpy(http->target, target);
00549       free(tmp);
00550     }
00551   } else if(!strcmp(token, "PUT")) {
00552     http->http_performative = HTTP_PUT;
00553     cur = http_GetToken(cur, &tmp);
00554     if (tmp != NULL) {
00555       http->target = (char*)malloc(sizeof(char)*(strlen(tmp)+1));
00556       strcpy(http->target, tmp);
00557       free(tmp);
00558     }
00559   } else if(!strcmp(token, "DELETE")) {
00560     http->http_performative = HTTP_DELETE;
00561   } else if(!strcmp(token, "TRACE")) {
00562     http->http_performative = HTTP_TRACE;
00563   } else if(!strcmp(token, "OPTIONS")) {
00564     http->http_performative = HTTP_OPTIONS;
00565   } else if(!strcmp(token, "CONNECT")) {
00566     http->http_performative = HTTP_CONNECT;
00567     /* FIXME */
00568   } else {
00569     /* Illegal performative */
00570     http->http_performative = HTTP_PERFORMATIVE_UNDEF;
00571   }
00572   free(token);
00573   cur = string;
00574   while(*cur != '\0') {
00575     if(*cur == '\n') {
00576       while (*cur == '\n' || *cur == '\r' || *cur == ' ')
00577         cur++;
00578       break;
00579     }
00580     cur++;
00581   }
00582   return cur;
00583 }
00584 
00585 const char*
00586 http_GetToken(const char* string, char** token)
00587 {
00588   const char* cur;
00589   const char* begin;
00590   char* itr;
00591   
00592   cur = string;
00593   /* See if it's an empty line */
00594   if (string[0] == '\r' && string[1] == '\n') {
00595     *token = NULL;
00596     return NULL;
00597   }
00598   /* Get rid of initial whitespace */
00599   while(*cur == ' ' || *cur == '\t' || *cur == '\r' || *cur == '\n') cur++;
00600 
00601   begin = cur;
00602   while(*cur != '\0') {
00603     cur++;
00604     if (*cur == ' ' || *cur == '\t' || *cur == '\r' || *cur == '\n')
00605       break;
00606   }
00607   cur--;
00608   *token = (char*)malloc(cur - begin + 4*sizeof(char));
00609   itr = *token;
00610   while (begin <= cur) {
00611     *itr = *begin;
00612     itr++;
00613     begin++;
00614   }
00615   *itr='\0';
00616   return cur+1;
00617 }
00618 
00619 int 
00620 mtp_http_ComposeMessage(message_p message)
00621 {
00622   char* http_header;
00623   char* tmp;
00624   char buf[20];
00625   if (message->isHTTP) {
00626     /* message reports that it already is an http message. No need to continue. */
00627     return 0;
00628   }
00629 
00630   http_header = (char*)malloc
00631     (
00632      sizeof(char) * (1400 + strlen(message->to_address))
00633     );
00634   http_header[0] = '\0';
00635   strcat(http_header, "POST /");
00636   strcat(http_header, message->target);
00637   strcat(http_header, " HTTP/1.0\r\n");
00638   strcat(http_header, "User-Agent: MobileC/");
00639   strcat(http_header, PACKAGE_VERSION);
00640   strcat(http_header, "\r\n");
00641   strcat(http_header, "Host: ");
00642   strcat(http_header, message->to_address);
00643   strcat(http_header, "\r\n");
00644   strcat(http_header, "Content-Type: text/plain\r\n");
00645   strcat(http_header, "Connection: Close\r\n");
00646   strcat(http_header, "Content-Length: ");
00647   sprintf(buf, "%d", strlen(message->message_body) + 1);
00648   strcat(http_header, buf);
00649   strcat(http_header, "\r\n\r\n");
00650 
00651   tmp = (char*)malloc
00652     (
00653      sizeof(char) * 
00654      (strlen(http_header) + strlen(message->message_body) + 1)
00655     );
00656   tmp[0] = '\0';
00657   strcpy(tmp, http_header);
00658   strcat(tmp, message->message_body);
00659   free(message->message_body);
00660   message->message_body = tmp;
00661   free(http_header);
00662   return MC_SUCCESS;
00663 }
00664 
00665 struct message_s* 
00666 mtp_http_CreateMessage(
00667     mtp_http_t* mtp_http,
00668     char* hostname,
00669     int port)
00670 {
00671   int i;
00672   int num;
00673   char buf[30];
00674   message_t* message;
00675   dynstring_t* http_message;
00676   http_message = dynstring_New();
00677   dynstring_Append(http_message, "POST /");
00678   dynstring_Append(http_message, mtp_http->target);
00679   dynstring_Append(http_message, " HTTP/1.0\r\n");
00680   dynstring_Append(http_message, "User-Agent: MobileC/");
00681   dynstring_Append(http_message, PACKAGE_VERSION);
00682   dynstring_Append(http_message, "\r\n");
00683   dynstring_Append(http_message, "Host: ");
00684   dynstring_Append(http_message, mtp_http->host);
00685   dynstring_Append(http_message, "\r\n");
00686 
00687   /* The next part of the message will be different depending on 
00688    * how many message parts we have.  */
00689   if(mtp_http->message_parts == 1) {
00690     dynstring_Append(http_message, "Content-Type: text/plain\r\n");
00691     dynstring_Append(http_message, "Connection: Close");
00692     dynstring_Append(http_message, "Content-Length: ");
00693     sprintf(buf, "%d", strlen((char*)mtp_http->content[0].data));
00694     dynstring_Append(http_message, buf);
00695     dynstring_Append(http_message, "\r\n\r\n");
00696     dynstring_Append(http_message, (char*)mtp_http->content[0].data);
00697   } else {
00698     /* We need to generate a random boundary. */
00699     srand(time(NULL));
00700     strcpy(buf, "--");
00701     for(i = 2; i < 26; i++) {
00702       num = rand() % 36;
00703       if(num < 10) {
00704         buf[i] = (char)(48 + num);
00705       } else {
00706         buf[i] = (char)( (num-10)+65);
00707       }
00708     }
00709     buf[i] = '\0';
00710     /* FIXME: A randomly generated boundary should now be stored in buf. Should
00711      * we check to see if there is a match within the body? The chances of collision are
00712      * infinitessimal. */
00713     dynstring_Append(http_message, "Content-Type: multipart-mixed ; boundary=\"");
00714     dynstring_Append(http_message, buf);
00715     dynstring_Append(http_message, "\"\r\n\r\n");
00716     dynstring_Append(http_message, "This is not part of the MIME multipart encoded message.\r\n");
00717     for(i = 0; i<mtp_http->message_parts; i++) {
00718       dynstring_Append(http_message, buf);
00719       dynstring_Append(http_message, "\nContent-Type: ");
00720       dynstring_Append(http_message, mtp_http->content[i].content_type);
00721       dynstring_Append(http_message, "\r\n\r\n");
00722       dynstring_Append(http_message, (char*)mtp_http->content[i].data);
00723       dynstring_Append(http_message, "\r\n\r\n");
00724     }
00725   }
00726   /* Append one last boundary */
00727   dynstring_Append(http_message, buf);
00728   dynstring_Append(http_message, "\n");
00729   message = message_New();
00730   message->to_address = (char*)malloc(
00731       sizeof(char) * (strlen(hostname)+15) );
00732   sprintf(message->to_address, "%s:%d", hostname, port);
00733   message->message_body = (char*) malloc( 
00734       sizeof(char) * (http_message->len + 1));
00735   strcpy(message->message_body, http_message->message);
00736   dynstring_Destroy(http_message);
00737   message->isHTTP = 1;
00738   return message;
00739 }
00740 

Generated on Fri May 16 14:49:54 2008 for Mobile-C by  doxygen 1.5.4