/* Nserver For Linux Copyright (C) 1999-2001 Sean Billings 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* nserver.c for Nokia Communicators */ /* comments or suggestions: nserver@syrinx.globalnet.co.uk */ #include #include #include #include #include #ifdef BSD #include #include #else #include #endif #include #ifdef SUN #include #else #include #endif #include #include #include #include "nserver.h" #include "crc.h" #include "charmap.h" #define VERSION "1.0.0" int porthandle; /*Handle to the serial port*/ unsigned int speed=DEFAULT_SPEED; char port[30]=DEFAULT_PORT; unsigned char echoback=0; unsigned char acknak=0; unsigned char last_RBACK=0; /*flag to indicate what RB ACK should go next*/ unsigned char last_HDR=1; /*flag to indicate what the last command header we used was*/ unsigned cmdready=0; /*flag to indicate if set that on a cmd1/2 to show the prompt*/ unsigned char msgShift=0; /*Shift indicator used by processMSG*/ unsigned char noLFCR=0; /*set to non zero to stop sendMessage prefixing a LF and CR*/ unsigned char NAKflag=0; /*indicates that the next message should append a NAK*/ unsigned char showconnect=1; /*indicates that 'connect' should be shown*/ unsigned char guimode=0; /*indicates that nserver is running under a GUI*/ char curdir[2048]; /*store NOKIA's version of the current directory*/ char realcwd[2049]; /*store the real cwd on startup (used as C:\NSERVER)*/ unsigned char olddir[2049]; /*used by filesize to backup the current dir*/ unsigned int debug=0; /*set to 1 for simple debugging and >1 to enable full debug output*/ void noop(){ /*just for FreeBSD???*/ } int openport(char *port, unsigned int speed){ int tty, i; #ifdef BSD struct termios attr; #else struct termio attr; #endif unsigned int speedy; switch(speed){ case 9600: speedy = B9600; break; case 19200: speedy = B19200; break; default: fputs("9600 or 19200 Please\n", stderr); exit(1); } if ((tty=open(port, O_RDWR|O_NONBLOCK)) == -1){ fputs("failed to open port.\n", stderr); exit(1); } #ifdef BSD ioctl(tty, TIOCGETA, &attr); #else ioctl(tty, TCGETA, &attr); #endif attr.c_iflag = IGNBRK; attr.c_oflag = 0; attr.c_lflag = 0; #ifdef SGI /* I have no idea if this works */ attr.c_cflag = speedy|CS8|HUPCL|CREAD|CNEW_RTSCTS; #else #ifdef BSD attr.c_cflag=CS8|HUPCL|CREAD|CRTSCTS; cfsetispeed(&attr, speedy); cfsetospeed(&attr, speedy); #else attr.c_cflag=speedy|CS8|HUPCL|CREAD|CRTSCTS; #endif #endif #ifndef BSD attr.c_line = 0; #endif #ifdef BSD for (i=0; i1) printf("sendByte %x\n",byte); } void sendMessage(char *buf,unsigned int bufsize){ /*sends text message buf in encapsulated form*/ unsigned short chksum=0x0000; unsigned int a; unsigned char sendbuf[BUF_SIZE]; /*transmission buffer*/ if(bufsize==0 && echoback==0 && acknak==0) return; /*exit if nothing to send*/ /*Set the header*/ sendbuf[0]=NOKIA_CMD; if(last_HDR==1){ sendbuf[1]=NOKIA_HDR1; last_HDR=0; }else{ sendbuf[1]=NOKIA_HDR2; last_HDR=1; } /*load the message into the buffer*/ if(!(echoback==0 && bufsize==0) && noLFCR==0){ /*prefix LF and CR*/ strcpy(&sendbuf[2],"\x0A\x0D"); for(a=0;a0) sendByte(NOKIA_CMD); /*used to esc a CMD char in the data*/ sendByte(sendbuf[a]); } /*calc and send the CRC*/ sendByte(NOKIA_CMD); sendByte(NOKIA_CHK); chksum=nserverCRC(sendbuf,bufsize); if((chksum & 0x00FF)==NOKIA_CMD) sendByte(NOKIA_CMD); /*esc the CMD char*/ sendByte(chksum & 0x00FF); /*LSB*/ if(((chksum & 0xFF00)>>8)==NOKIA_CMD) sendByte(NOKIA_CMD); /*esc the CMD char*/ sendByte((chksum & 0xFF00)>>8); /*MSB*/ } void sendCode(unsigned char *buf, unsigned int bufsize){ /*sends verbatim characters to the Nokia*/ unsigned int a; for(a=0;a0) eom[strlen(eom)]=tmpbyte; if(strlen(eom)>4) for(a=0;a<10;a++) eom[a]='\0'; /*check for 0x9595 data*/ if(tmpbyte==NOKIA_CMD) if(ESCflag!=0){ ESCflag=0; /*previous char was 0x95 so ignore this one*/ IgnoreCMD=1; }else{ buf[bufl++]=tmpbyte; /*add the received char to the buffer*/ ESCflag=1; /*set flag to indicate last char was 0x95*/ IgnoreCMD=0; /*and reset Escaped CMD flag*/ } else{ buf[bufl++]=tmpbyte; /*add the received char to the buffer*/ ESCflag=0; /*clear flag*/ } if(IgnoreCMD!=0 && noIgnore!=0) IgnoreCMD=0; if(IgnoreCMD==0){ /*only do checks if last CMD wasn't escaped*/ if(bufl>2) /*10/09/99 special check to prevent the ignore flag being set*/ if(buf[bufl-2]==NOKIA_CMD && buf[bufl-1]==NOKIA_CHK) noIgnore=1; if(bufl==4) if(strncmp(buf,NOKIA_CMD1,4)==0 || strncmp(buf,NOKIA_CMD2,4)==0) gotchk=1; if(bufl>4){ tmpbyte=NOKIA_CMD; if(buf[bufl-4]==tmpbyte){ tmpbyte=NOKIA_CHK; if(buf[bufl-3]==tmpbyte){ gotchk=1; /*validate the chksum*/ if(buf[0]==NOKIA_CMD && (buf[1]==NOKIA_HDR1 || buf[1]==NOKIA_HDR2)){ /*but only if it starts correctly*/ chksum=buf[bufl-1] *0x100; chksum+=buf[bufl-2]; if(chksum!=nserverCRC(&buf[0],bufl-4)){ if(debug){ printf("msg failed CRC\n"); for(a=0;a=strlen(NOKIA_EXIT)) if(strncmp(&buf[bufl-3],NOKIA_EXIT,strlen(NOKIA_EXIT))==0) gotchk=1; } }while(gotchk==0); return(bufl); } unsigned int stripMessage(unsigned char *buf,unsigned int bufsize){ /*removes the encapsulation*/ /*re-written 16/10/99*/ unsigned int a,b,c,d,e; unsigned int newbufsize=0; unsigned int wastecount=0; unsigned char newbuf[BUF_SIZE]; unsigned int HDRs[100]; unsigned int CHKs[100]; unsigned int nextHDR=0; unsigned int nextCHK=0; if(debug>1){ printf("stripMessage:"); for(a=0;a3){ for(a=0;a0 && nextHDR>0){ for(e=0;e<1;e++){ /*remove any false HDRs or CHKs*/ b=0; for(a=0;a0) b++; for(c=a+1;c0;a--){ /*remove any 'spare' CHKs within HDR-CHK encapsulation*/ if(b>0 && aHDRs[b] && a>0){ /*safety check*/ if(CHKs[a-1]>HDRs[b]){ for(c=a-1;c0 && nextCHK>0){ nextHDR=1; CHKs[0]=CHKs[nextCHK]; nextCHK=1; } } if(debug>1){ printf("Headers: "); for(a=0;abufsize) return(0); /*not enough chars left*/ for(a=0;a0); return(0); } void showunknown(unsigned char *msg,unsigned int size,char *dbg){ unsigned int a; if(debug==0) return; printf("%s:",dbg); for(a=0;a13) strcpy(&tmp[3],&curdir[13]); } strcpy(&tmp[strlen(tmp)],">;"); sendMessage(tmp,strlen(tmp)); if(debug) printf("%s>\n",curdir); } else sendMessage("",0); } void do_cmd1(void){ if(debug) printf("NOKIA_CMD1\n"); if(cmdready==1) show_prompt(); cmdready=0; last_HDR=0; } void do_cmd2(void){ if(debug) printf("NOKIA_CMD2\n"); if(cmdready==1) show_prompt(); cmdready=0; last_HDR=1; } void do_ebon(void){ if(debug) printf("Echoback = on\n"); send_RBACK(); cmdready=1; sendMessage("Echoback = on",13); echoback=1; } void do_eboff(void){ if(debug) printf("Echoback = off\n"); send_RBACK(); cmdready=1; echoback=0; } void do_akon(void){ if(debug) printf("ACK/NAK = on\n"); send_RBACK(); cmdready=1; if(echoback==1) sendMessage("ACK/NAK = on",12); else{ acknak=1; sendMessage("",0); } acknak=1; } void do_akoff(void){ if(debug) printf("ACK/NAK = off\n"); acknak=0; send_RBACK(); cmdready=1; if(echoback==1) sendMessage("ACK/NAK = off",13); } void fixfile(char *file){ /*fixes the filespec from DOS to unix file must be a null teminated string*/ unsigned int a; /*first up remove C:\NSERVER\*/ if(cmdCompare(file,0,strlen(file),"C:\\NSERVER\\",11)!=0) strcpy(&file[0],&file[11]); if(cmdCompare(file,0,strlen(file),"C:\\NSERVER",10)!=0) file[0]='\0'; /* below mod by Panu Matilainen so you no longer need a C directory underneath the nserver one. -note from SCB you can now however set your communicator to use D:\ if you do want a 'safe dir'. */ if(cmdCompare(file,0,strlen(file),"C:\\",3)!=0) file[0]='\0'; /*now check for drive root access*/ if(cmdCompare(file,0,strlen(file),"C:",2)!=0) strcpy(&file[1],&file[2]); /*get rid of ':'*/ if(cmdCompare(file,0,strlen(file),"D:",2)!=0) strcpy(&file[1],&file[2]); /*get rid of ':'*/ /*now convert any backslashes to forwardslashes*/ for(a=0;a0;a--){ if(searchdir[a]=='/'){ searchdir[a]='\0'; /* found end of path */ strncpy(filename,&searchdir[a+1],255); break; } } if(strcmp(searchdir,".")!=0) strcpy(searchdir,&searchdir[2]); if(debug) printf("fixcase: (%s) [%s] [%s]\n",file,searchdir,filename); if ((dp=opendir(searchdir)) == NULL) { if(debug) printf("error opening dir: %s\n",searchdir); return -1; } while ((dirp=readdir(dp)) != NULL) { if(debug) printf("fixcase dir entry: %s\n",dirp->d_name); if (strcasecmp(filename, dirp->d_name) == 0) { if (debug) printf("%s matches %s\n", filename, dirp->d_name); /*rebuild filespec*/ if(strcmp(searchdir,".")==0) strcpy(file,dirp->d_name); else sprintf(file,"%s/%s",searchdir,dirp->d_name); closedir(dp); return 0; } } closedir(dp); return 0; } void do_CD(unsigned char *msg,unsigned int size){ char newdir[BUF_SIZE]; unsigned int a; unsigned int newdirst=0; int cderr=0; geos2unix(msg); /*convert to unix charset*/ /*if .. and already at startup directory prevent going back further*/ if(size<4 && msg[0]=='.' && msg[1]=='.'){ getcwd(newdir,1024); if(strcmp(realcwd,newdir)==0){ if(olddir[0]!='\0' && strcmp(olddir,realcwd)!=0){ chdir(olddir); updatecwd(); olddir[0]='\0'; /*finished with the backup dir*/ }else size=1; /*sets CD request to . */ if(debug) printf("CD: attempt to go back too far\n"); } } /*check to see if path is relative*/ if(size>1 && msg[1]==':') strcpy(newdir,realcwd); else getcwd(newdir,1024); strcpy(&newdir[strlen(newdir)],"/"); newdirst=strlen(newdir); for(a=0;a='a' && msg[a]<='z') fspec[a+b]=msg[a]-32; /*Ucase it*/ else fspec[a+b]=msg[a]; } if(debug) printf("\n"); /*now expand * into multiple ?*/ for(a=0;a<12;a++) if(fspec[a]=='*') if(a<8) for(;a<8;a++) fspec[a]='?'; else if(a>8) for(;a<13;a++) fspec[a]='?'; n=scandir(".",&namelist,0,alphasort); if(n<0){ perror("scandir"); return; } acknak=0; /*this will be switched back on later*/ sprintf(opbuf,"Directory of %s",curdir); for(a=0;ad_name);b++){ /*expand fname for easy comparison*/ if(namelist[a]->d_name[b]=='.') c=8-b; /*calc offset*/ if(namelist[a]->d_name[b]>='a' && namelist[a]->d_name[b]<='z') fname[b+c]=namelist[a]->d_name[b]-32; /*Ucase it*/ else fname[b+c]=namelist[a]->d_name[b]; } /*support for files with no extensions:*/ if(fname[8]!='.') fname[8]='.'; c=0; /*offset used to match the file extension*/ for(b=0;b<12;b++){ if(fname[b]=='.') c=8-b; /*calc the offset*/ if(fspec[b+c]!='?'){ /*if not a wildcard*/ if(fspec[b+c]!=fname[b]) break; /*abort as it doesn't match*/ } if(b==11){ /*a match*/ strncpy(fname,namelist[a]->d_name,12); /*re copy the filename as we butchered it earlier*/ fname[12]=0; stat(fname,&stats); /*get useful file info*/ modtime=*localtime(&stats.st_mtime); /*modification time structure*/ hr=modtime.tm_hour-1; /*some 24 to 12 hour conversion*/ if(hr>12){ hr=hr-12; ampm='p'; }else{ ampm='a'; if(hr==-1) hr=12; } umode=stats.st_mode; strcpy(attribs,"a-----"); if(umode & 0x4000) /*if it is a directory*/ attribs[1]='d'; /*sprintf split up to prevent core dump on FreeBSD*/ unix2geos(fname); /*convert to geos charset*/ /*Y2K fix to prevent year 100*/ modyear=modtime.tm_year; if(modyear>99) modyear=modyear-100; sprintf(&opbuf[strlen(opbuf)],"\012\015%-12s %8ld %02d-%02d-%02d %2d:%02d%c ",fname, stats.st_size,modtime.tm_mon+1,modtime.tm_mday,modyear,hr,modtime.tm_min,ampm); sprintf(&opbuf[strlen(opbuf)],"%s *NON_GEOS*",attribs); dirlines++; if(dirlines>=7){ /*send seven lines at a time*/ if(debug) printf("%s",opbuf); sendMessage(opbuf,strlen(opbuf)); opbuf[0]='\0'; /*trash the output buffer*/ dirlines=0; /*reset the counter*/ noLFCR=1; /*prevent any more of these being prefixed*/ } } } } if(strlen(opbuf)>0 && dirlines>0){ /*if there are any lines left in the buffer send 'em*/ if(debug) printf("%s\n",opbuf); sendMessage(opbuf,strlen(opbuf)); } /*reset the flags we messed up etc*/ acknak=1; cmdready=1; noLFCR=0; } void do_RK(void){ if(debug) printf("RK\n"); send_RBACK(); /*I am not sure of this is what RK is for*/ last_RBACK=0; last_HDR=1; } void do_FZ(void){ if(debug) printf("FZ\n"); send_RBACK(); msgShift='Z'; /*notify processMSG that the next command is for file size*/ } void do_FileSize(char *msg){ struct stat stats; unsigned char size[4]; /*size is stored in 4 bytes LSB first*/ unsigned long fsize; send_RBACK(); geos2unix(msg); /*convert geos to unix charset*/ fixfile(msg); fixcase(msg); if(stat(msg,&stats)==-1){ /*if file not found*/ /*the NOKIA assumes we are in the root dir even though it may have changed dir*/ getcwd(olddir,2048); /*backup the cwd*/ if(debug) printf("AUTO CD from %s\n",olddir); chdir(realcwd); updatecwd(); if(stat(msg,&stats)==-1){ fsize=0; if(debug) printf("Restoring directory to: %s\n",olddir); chdir(olddir); /*restore the cwd*/ updatecwd(); } else fsize=stats.st_size; }else fsize=stats.st_size; if(debug) printf("FILE:%s FZ=%ld\n",msg,fsize); if(fsize==0){ noLFCR=1; if(last_HDR==0) sendCode(NOKIA_NF1,8); else sendCode(NOKIA_NF2,8); noLFCR=0; }else{ size[0]=fsize & 0xff; size[1]=(fsize & 0xff00)/0x100; size[2]=(fsize & 0xff0000)/0x10000; size[3]=(fsize & 0xff000000)/0x1000000; noLFCR=1; sendMessage_ACK(size,4); noLFCR=0; } } void pccommget(char *msg){ /*send file msg to the Nokia*/ struct stat stats; unsigned char buf[BUF_SIZE]; unsigned long fsize; int fh; unsigned int a; unsigned int bytes=0; unsigned char retry=0; /*retry flag*/ send_RBACK(); geos2unix(msg); /*convert to unix charset*/ fixfile(msg); fixcase(msg); /*open the file*/ if((fh=open(msg,O_RDONLY))==-1){ printf("Failed to open: %s\n",msg); return; /*exit out if file cannot be opened*/ } stat(msg,&stats); fsize=stats.st_size; if(debug) printf("sending file %s size:%ld\n",msg,fsize); if(guimode) printf("Sending file %s :\n",msg); noLFCR=1; if(debug) printf("sending NOKIA_SYNC\n"); if(!sendMessage_ACK(NOKIA_SYNC,1)){ /*start code*/ close(fh); return; } /*send the filename+size excluding path*/ strcpy(buf,msg); /*copy the full filespec first in case it has no path*/ unix2geos(buf); /*convert back to geos charset*/ for(a=strlen(msg)-1;a>0;a--) if(msg[a]=='\\'){ a++; strcpy(buf,&msg[a]); break; } /*now the size*/ a=strlen(buf); buf[++a]=fsize & 0xff; buf[++a]=(fsize & 0xff00)/0x100; buf[++a]=(fsize & 0xff0000)/0x10000; buf[++a]=(fsize & 0xff000000)/0x1000000; if(debug) printf("sending filename and size\n"); if(!sendMessage_ACK(buf,a+1)){ close(fh); return; } usleep(500); /*just wait a tick*/ /*now send the file*/ if(debug) printf("Now sending the file\n"); a=1; do{ if(retry==0) bytes=read(fh,buf,PKT_SIZE); else{ retry=0; } if(bytes>0){ a++; if(sendMessage_ACK(buf,bytes)) fsize-=bytes; else{ retry=1; a--; } } if(guimode==0){ if(a>2) printf("\x0d%ld bytes left for %s... ",fsize,msg); } }while(fsize>0); if(a>2 && guimode==0) printf("\n"); if(debug) printf("eof\n"); if(!sendMessage_ACK("\x00",1)){ close(fh); return; } if(debug) printf("sent 0x02\n"); sendMessage_ACK("\x02",1); noLFCR=0; /*finish*/ close(fh); if(guimode){ printf("*[up]*\n"); printf("Sending file %s : Transfer successful.\n",msg); } } void do_XF01(){ /*initiate system to commsend*/ if(debug) printf("XF 0x01\n"); send_RBACK(); msgShift='X'; } void pccommsend(unsigned char *msg){ /*Receive file from the Nokia*/ unsigned char buf[BUF_SIZE]; unsigned long fsize; unsigned int a; unsigned int bufsize; int fh; unsigned int bytes; a=strlen(msg)+1; fsize=msg[a++]; fsize+=msg[a++] * 0x100; fsize+=msg[a++] * 0x10000; fsize+=msg[a] * 0x1000000; geos2unix(msg); /*convert geos to unix charset*/ if(debug) printf("Receiving file: %s size:%ld\n",msg,fsize); if(guimode) printf("Receiving file %s :\n",msg); /*open file for output*/ if((fh=open(msg,O_WRONLY | O_CREAT | O_TRUNC,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH))==-1){ printf("failed to create: %s\n",msg); return; } send_RBACK(); while(fsize>0){ if((bufsize=getMessage(buf))!=0) if(buf[0]!=NOKIA_CMD) bufsize=0; else{ /*don't use stripMessage as it can trash the file if it contains a 0x95*/ bufsize-=6; for(a=0;a0){ bytes=write(fh,buf,bufsize); if(bytesfsize){ if(debug) printf("error too many bytes received %d/%ld\n",bufsize,fsize); fsize=0; }else fsize-=bufsize; if(guimode==0){ printf("\x0d%ld left for file %s... ",fsize,msg); if(fsize==0) printf("\n"); } send_RBACK(); } } /*wait for next message (which should be 0x80)*/ do{ bufsize=getMessage(buf); if(bufsize>0) bufsize=stripMessage(buf,bufsize); if(bufsize==1 && buf[0]==0x80){ send_RBACK(); break; }else{ if(debug){ printf("Failed to get 0x80 message\n"); for(a=0;a1){ printf("in-msg:"); /*debug code*/ for(a=0;a2){ if(strcmp("--debug",argv[a])==0 || strcmp("-d1",argv[a])==0) debug=1; if(strcmp("-d2",argv[a])==0) debug=2; if(strcmp("-gui",argv[a])==0) guimode=1; if(strcmp("-port",argv[a])==0) if(a+1