Logo Search packages:      
Sourcecode: agsync version File versions  Download package

syncmain.c

/*****************************************************************************
 * Avantgo synchronization tool for Pocket PCs under Linux.
 * The 'plumbing' to the Avantgo libs that will allow a CE device to
 * sync to a Linux desktop. This is just a proof-of-concept right
 * now to show how easy it is. Later, we will make this a beautiful
 * implementation that does good things.
 */


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is "agsync"
 *
 * The Initial Developer of the Original Code is
 * Michael Jarrett
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   David Eriksson
 *
 * ***** END LICENSE BLOCK ***** */



#include <unistd.h>
#include <stdio.h>
#include <string.h>

#include <rapi.h>

#include <AGTypes.h>
#include <AGReader.h>
#include <AGWriter.h>
#include <AGBufferReader.h>
#include <AGNet.h>
#include <AGRecord.h>
#include <AGUserConfig.h>
#include <AGDBConfig.h>
#include <AGServerConfig.h>
#include <AGDeviceInfo.h>
#include <AGCommandProcessor.h>
#include <AGClientProcessor.h>
#include <AGLocationConfig.h>

#include "syncstream.h"

/* Verbose level.
    0 - Normal.
    5 - High
*/
int verboseLevel= 0;


/* Location config for proxy */
AGLocationConfig *locConfig= NULL;

/********** Platform functions **********/
/* Please see AGClientProcessor.h for specifications of the interfaces
   of these functions.
*/
AGRecord *pRecord= NULL;

typedef struct _PStoreStruct {
  AGReader *r;
  AGWriter *w;
  AGCommandProcessor *cmdProc;
} PStoreStruct;

PStoreStruct pStore;

int32 pNextModifiedRecord(void *pStoreVoid, AGRecord **record, int32 *errCode){
  int res;
  if(NULL != pRecord)
    AGRecordFree(pRecord);

  res= asGetNextModifiedRecord(((PStoreStruct *)pStoreVoid)->r,
                         ((PStoreStruct *)pStoreVoid)->w,
                         record);
  if(2 == res)
    *errCode= asErrno;

  return res;
}

int32 pNextRecord(void *pStoreVoid, AGRecord **record, int32 *errCode) {
  int res;
  if(NULL != pRecord)
    AGRecordFree(pRecord);

  res= asGetNextRecord(((PStoreStruct *)pStoreVoid)->r,
                   ((PStoreStruct *)pStoreVoid)->w,
                   record);
  if(2 == res)
    *errCode= asErrno;

  return res;
}

int32 pOpenDatabase(void *pStoreVoid, AGDBConfig *theDatabase, int32 *errCode){
  int res;
  res= asOpenDatabase(((PStoreStruct *)pStoreVoid)->r,
                  ((PStoreStruct *)pStoreVoid)->w,
                   theDatabase);
  if(2 == res)
    *errCode= asErrno;

  return res;
}

int32 pNextExpansionCommand(void *pStoreVoid, int32 *cmd,
                      int32 *cmdLen, void **cmdData) {
  // I suspect that the regular case does not use this.
  return 0;
}

int32 pPerformCommand(void *pStoreVoid, int32 *err, AGReader *r) {
  int result;
  unsigned char *data;
  int dataLen, cmd;
  AGBufferReader *rB= (AGBufferReader *)r;

  AGPerformCommandFunc cmdFunc= AGCommandProcessorGetPerformFunc(
                             ((PStoreStruct *)pStoreVoid)->cmdProc);
  
  result= (*cmdFunc)(((PStoreStruct *)pStoreVoid)->cmdProc, err, r);
  
  /* I know it's a BufferReader, and when was the last time C++
     allowed be to h4x0r object internals? Heh. Lets enjoy it! */
  rB->currentIndex= 0;
  cmd= AGReadCompactInt(r);
  dataLen= AGReadCompactInt(r);
  data= (unsigned char *) (rB->buffer + rB->currentIndex);

  // All commands except goodbye should return 0
  if(1 != result && 0 != cmd && 1 < verboseLevel)
    printf("ERROR ON COMMAND %d.\n", cmd);
  
  asPerformCommand(((PStoreStruct *)pStoreVoid)->r,
                    ((PStoreStruct *)pStoreVoid)->w,
                    cmd, data, dataLen);
  return result;
}


AGPlatformCalls pCalls= {
  (void *)&pStore,
  pNextModifiedRecord,
  pNextRecord,
  pOpenDatabase,
  pNextExpansionCommand,
  (void *)&pStore,
  pPerformCommand
};

/********** End platform stuff **********/

char spinState= '0';

int32 taskPrinter(void *blah, int *errBlah, char *str, AGBool thing) {
  if(spinState != '0') {
    spinState= '0';
    printf("\x01B[A\x01B[G                                                   "
         "\x01B[G");    
  }
  printf("%s\n", str);
  return 1; // AGCLIENT_CONTINUE
}


int32 itemPrinter(void *blah, int *errBlah, int item, int total, char *name) {
  int pos, i;
  if(0 == total) // Boring, and bad for division
    return 1;

  if('0' != spinState) {
    printf("\x01B[A\x01B[G");
  }
  printf("[");

  pos= item * 25 / total;
  for(i= 0; i < 25; i++) {
    if(i < pos)
      printf("#");
    else
      printf(" ");
  }

  printf("] (%d of %d)            \n", item, total);
  spinState= '/';
  
  return 1; // AGClIENT_CONTINUE
}


/**
 * Synchronize an individual server.
 */
void doServerSync(AGReader *r, AGWriter *w, AGServerConfig *s, AGNetCtx *ctx) {
  int result;
  AGCommandProcessor *cmdProc;

  if(s->disabled) {
    if(2 < verboseLevel)
      printf("Skipping disabled server %s\n", s->friendlyName);
    return;
  }

  if(1 < verboseLevel)
    printf("Synchronizing \"%s\"\n", s->friendlyName);

  /* Initialize the command processor */
  cmdProc= AGCommandProcessorNew(s);
  pStore.cmdProc= cmdProc;
  /* Note: If we wanted status messages, we could hook the
           command processor here to handle them.
  */
  if(1 < verboseLevel) {
    cmdProc->commands.performTaskFunc= taskPrinter;
    cmdProc->commands.performItemFunc= itemPrinter;
  }


  /* Start server block */
  if(0 != asStartServer(r, w, s->uid)) {
    if(1 < verboseLevel)
      printf("AvantGo error on asStartServer: %d!\n", asErrno);
    return;
  }

  /* A loop over server connections, since we may have to sync
     more than once. This normally doesn't happen, but I think is
     used occasionally to initialize a device which has no config yet.
  */
  do {
    AGDeviceInfo *devInfo;
    AGClientProcessor *clientProc;

    if(3 < verboseLevel)
      printf("Beginning synchonization attempt on server.\n");

    devInfo= AGDeviceInfoNew();
    
    if(NULL == asGetDeviceInfo(r, w, devInfo)) {
      if(0 < verboseLevel)
      printf("Failed to retrieve device information!\n");
      goto devEnd;
    }

    AGCommandProcessorStart(cmdProc); /* Error code unused */
    
    /* Set up client processor...
       TRUE is for buffering of cmds. */
    clientProc= AGClientProcessorNew(s, devInfo, locConfig, &pCalls,
                             TRUE, ctx);
    /* Apparently we dont?? */
    AGClientProcessorSetBufferServerCommands(clientProc, FALSE);


    /* Basically a connect request? */
    AGClientProcessorSync(clientProc);

    
    /* Perform the synchronization */
    do {
      result= AGClientProcessorProcess(clientProc);
    } while(AGCLIENT_CONTINUE == result);

  cpEnd:
    AGClientProcessorFree(clientProc);

  devEnd:
    AGDeviceInfoFree(devInfo);

  } while(AGCommandProcessorShouldSyncAgain(cmdProc));

  AGCommandProcessorFree(cmdProc);


  if(0 != asEndServer(r, w)) {
    if(1 < verboseLevel)
      printf("Avantgo error on asEndServer: %d!\n", asErrno);
  }
}


/**
 * Performs the full synchronization process, given
 * valid readers and writers. Does not close the stream.
 */
void doSync(AGReader *r, AGWriter *w, AGNetCtx *ctx) {
  AGUserConfig *userConfig;
  int cnt, i;

  /* Read configuration */
  userConfig= AGUserConfigNew();

  /* TODO: Most sync. apps will read a configuration from disk here,
           determine an "agreed" configuration, and push that back
         to the device. We should do this eventually.
  */
  if(NULL == asGetUserConfig(r, w, userConfig)) {
    if(0 < verboseLevel)
      printf("Failed to receive user configuration from device.\n");

    goto confEnd;
  }


  /* Loop through servers and sync. */
  cnt= AGUserConfigCount(userConfig);  /* Implied (Server)Count */
  if(2 < verboseLevel)
    printf("Processing %d servers.\n", cnt);
  for(i= 0; i < cnt; i++) {
    doServerSync(r, w, AGUserConfigGetServerByIndex(userConfig, i), ctx);
  }

  /* TODO: Write updated config */
  if(0 != asPutUserConfig(r, w, userConfig)) {
    if(0 < verboseLevel)
      printf("Failed to store user configuration to device.\n");
  }

 confEnd:
  AGUserConfigFree(userConfig);
}



/**
 * Connects to the device, and establishes communication with
 * the AvantGo .DLL on the device.
 * If there is an error, the connections will be cleaned up
 * before returning NULL.
 *
 * @return An IRAPIStream set up for direct communication
 * with the AvantGo DLL, or NULL if the connection failed.
 */
IRAPIStream *openAGStreamProtocol() {
  HRESULT hr;
  IRAPIStream *stream;

  if(3 < verboseLevel)
    printf("Connecting to device.\n");

  hr = CeRapiInit();
  if (FAILED(hr))
    return NULL;

  if(3 < verboseLevel)
    printf("Sending CeRapiInvoke.\n");

  hr = CeRapiInvokeA(
      "malclmgr.dll", 
      "_RAPI_HandleStream2",
      0,
      NULL,
      0,
      NULL,
      &stream,
      0);

  if (FAILED(hr))
  {
    CeRapiUninit();
    return NULL;
  }

  return stream;
}


/**
 * Closes the stream.
 */
int closeAGStreamProtocol(IRAPIStream* s) {
  if(3 < verboseLevel)
    printf("Disconnecting from DLL.\n");
  return IRAPIStream_Release(s);
}


/**
 * Stream read function.
 */
static int32 readFunc(void *s, void *data, int32 len) {
  HRESULT hr = IRAPIStream_Read((IRAPIStream*)s, data, len, NULL); 
  if (SUCCEEDED(hr))
    return len;
  else
    return 0;
}

/**
 * Stream write function
 */
static int32 writeFunc(void *s, void *data, int32 len) {
  HRESULT hr = IRAPIStream_Write((IRAPIStream*)s, data, len, NULL); 
  if (SUCCEEDED(hr))
    return len;
  else
    return 0;
}



/** Displays usage */
void usage(int argc, char* argv[]) {
  printf("Usage: %s <options>\n", argv[0]);
  printf("Options:\n");
  printf("  -v  Verbose output (use multiple times for even more output)\n");
  printf("  -q  Quiet mode (use multiple times to kill all output)\n");
  printf("  -p host:port       HTTP proxy.\n");
  printf("  -u username:pass   Username/password for HTTP proxy;\n");
  printf("  -s host:port       Socks proxy.\n");
}


/**
 * Parses arguments, and acts upon them.
 * @return 1 on success, otherwise 0
 */
short parseArguments(int argc, char *argv[]) {
  int i, mode= 0;

  for(i= 1; i < argc; i++) {
    if(0 != mode) { // Conditions for complex params
      char *colon;
      colon= strchr(argv[i], ':');
      // Note: We MUST strdup param strings, since AGLCFree will free them!
      switch(mode) {
      case 1:
      if(NULL == colon)
        return 0;
      if(NULL == locConfig)
        locConfig= AGLocationConfigNew();
      *colon= 0;
      locConfig->HTTPName= strdup(argv[i]);
      locConfig->HTTPPort= atoi(colon+1);
      locConfig->HTTPUseProxy= 1;
      *colon= ':';
      break;
      case 2:
      if(NULL == colon)
        return 0;
      if(NULL == locConfig)
        locConfig= AGLocationConfigNew();
      *colon= 0;
      locConfig->HTTPUsername= strdup(argv[i]);
      locConfig->HTTPPassword= strdup(colon+1);
      locConfig->HTTPUseAuthentication= 1;
      *colon= ':';
      break;
      case 3:
      if(NULL == colon)
        return 0;
      if(NULL == locConfig)
        locConfig= AGLocationConfigNew();
      *colon= 0;
      locConfig->SOCKSName= strdup(argv[i]);
      locConfig->SOCKSPort= atoi(colon+1);
      locConfig->SOCKSUseProxy= 1;
      *colon= ':';
      break;
      }
      mode= 0;
    }
    else if('-' == argv[i][0]) { // Switches
      switch(argv[i][1]) {
      case 'v':
      verboseLevel++;
      break;
      case 'q':
      verboseLevel--;
      break;
      case 'p':
      mode= 1;
      break;
      case 'u':
      mode= 2;
      break;
      case 's':
      mode= 3;
      break;
      default:
      return 0;
      }
    }
    else {  // Other stuff
      return 0;
    }
  }

  if(0 != mode)
    return 0;

  return 1;
}


/**
 * main.
 * Don't make me explain this.
 */
int main(int argc, char *argv[]) {
  IRAPIStream *s;
  int result;
  AGReader *r;
  AGWriter *w;
  AGNetCtx ctx;

  /* Set verbose level to default */
  verboseLevel= 2;

  if(!parseArguments(argc, argv)) {
    usage(argc, argv);
    exit(1);
  }

  /* Connect */
  s= openAGStreamProtocol();
  if(NULL == s) {
    if(0 < verboseLevel)
      printf("Unable to connect to remote device!\n");
    exit(1);
  }

  /* Set up reader and writer */
  r= AGReaderNew((void *)s, readFunc);
  w= AGWriterNew((void *)s, writeFunc);
  pStore.r= r; pStore.w = w;


  /* Start AGNetCtx. This is a structure containing function pointers
     for all the basic socket I/O.
     The more advanced MAL implementations can load a shared library
     for this here. The base library from AGNet.c seems adequate,
     so we won't bother here.
     There's a lot of WIN32 code, but all ifdef'd.
  */
  AGNetInit(&ctx);

  /* Do work */
  doSync(r, w, &ctx);

  /* Do end session */
  result= asEndSession(r, w);
  if(3 < verboseLevel) {
    printf("End command result: %d\n", result);
  } else if(1 < verboseLevel && result != 0) {
    printf("Error on asEndSession: %d\n", asErrno);
  }

  /* Close up */
  AGNetClose(&ctx);
  AGWriterFree(w);
  AGReaderFree(r);

  if(NULL != locConfig)
    AGLocationConfigFree(locConfig);

  if(NULL != pRecord)
    AGRecordFree(pRecord);

  closeAGStreamProtocol(s);

  return 0;
}

Generated by  Doxygen 1.6.0   Back to index