| /* |
| * Copyright (C) 2006-2023 S&S |
| * Samuel Thibault <samuel.thibault@ens-lyon.org> |
| * Sébastien Hinderer <sebastien.hinderer@ens-lyon.org> |
| * |
| * 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 the program ; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| /* |
| * vstp_transfer.c |
| * |
| * handles file transfers |
| */ |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| |
| #include "brlapi.h" |
| #include "brlapi_protocol.h" |
| |
| #include "vstp.h" |
| |
| #ifdef __MINGW32__ |
| #define alarm(t) (void)0 |
| #endif |
| |
| /* SEND */ |
| /* tries to send a message */ |
| #define SEND(buf,size) \ |
| do {\ |
| if (brlapi_sendRaw(buf,size)<0) {\ |
| perror("while sending");\ |
| transfer_abort(RET_ECONN);\ |
| }\ |
| }\ |
| while (0) |
| |
| /* RECV */ |
| /* tries to get a message */ |
| /* on error, try to clean */ |
| /* also set a timeout */ |
| #define RECV() \ |
| while (1) {\ |
| alarm(TRY_TIMEOUT);\ |
| res=brlapi_recvRaw(ibuf,BRLAPI_MAXPACKETSIZE);\ |
| alarm(0);\ |
| if (res<0) {\ |
| perror("while receiving");\ |
| transfer_abort(RET_ECONN);\ |
| }\ |
| if (ibuf[0]==VB_RESET[0]) {\ |
| fprintf(stderr,"transfer interrupt by user !\n");\ |
| transfer_abort(RET_INT);\ |
| }\ |
| if (res) break;\ |
| } |
| |
| int transferring = 0; |
| unsigned char burstmode = VB_AUTOMATIC; |
| int backup = 0; |
| char *visiobases_dir = NULL; |
| char *visiobases_ext = ".vis"; |
| static unsigned char numpacket; |
| static int sizetransferred; |
| static char ibuf[BRLAPI_MAXPACKETSIZE]; |
| static int osize; |
| static int otries; |
| static char obuf[BRLAPI_MAXPACKETSIZE]; |
| static unsigned char filename[VB_MAXFNLEN+4+1]; |
| |
| /* showPacket */ |
| /* eventually show the packet and abort, if tries number is reached */ |
| |
| static void showPacket(int size) { |
| int i; |
| fprintf(stderr,"unexpected %c packet, size %d, content :\n", ibuf[0], size); |
| for (i=0;i<size;i++) { |
| fprintf(stderr,"%2x ", ibuf[i]); |
| if (!((i+1)%10)) fprintf(stderr,"\n"); |
| } |
| if (otries++<NUM_TRIES) return; |
| fprintf(stderr,"couldn't recover, so aborting...\n"); |
| transfer_abort(RET_EPROTO); |
| } |
| /* WaitForOk */ |
| /* wait for "Y" packet */ |
| static void WaitForOk(void) { |
| int res; |
| do { SEND(obuf,osize); RECV() } |
| while ((res==0 || ibuf[0]!=VB_OK) && (showPacket(res),1)); |
| } |
| |
| /* renameBackups */ |
| /* renames backups, increasing their number, like logrotate does */ |
| /* returns value returned by the last rename call */ |
| int renameBackups(const char *filename) { |
| #define SIZEMAX_SUFFIX (1+20+1) |
| int n=strlen(filename); |
| char to[n+SIZEMAX_SUFFIX],from[n+SIZEMAX_SUFFIX]; |
| struct stat st; |
| int i,max; |
| |
| memcpy(to,filename,n); |
| for (max=1;max;max++) { |
| sprintf(to+n,".%c",max); |
| if (stat(to,&st)<0) break; |
| } |
| if (!max) { |
| fprintf(stderr,"too many backups for %s\n",filename); |
| errno=ENAMETOOLONG; |
| return -1; |
| } |
| memcpy(from,filename,n); |
| strcpy(from+n,".1"); |
| i=max-1; |
| while(i) { |
| sprintf(from+n,".%u",i); |
| if (rename(from,to)<0) { |
| fprintf(stderr,"%s -> %s failed\n",from,to); |
| return -1; |
| } |
| if (--i) strcpy(to+n,from+n); |
| else break; |
| } |
| return(rename(filename,from)); |
| } |
| |
| /* fileget */ |
| /* get file f on VisioBraille */ |
| void fileget(char *f) { |
| int n=strlen(f),lnpath,lnext; |
| char *c,*d; |
| int res; |
| int fd; |
| struct stat st; |
| char *path,*ext; |
| |
| if (n==0) return; |
| |
| /* find path, if any */ |
| for (c=f+n-1; c>=f && *c!='/'; c--); |
| if (c>=f) { |
| path=f; |
| lnpath=c-f; |
| *c++='\0'; |
| f=c; |
| } else { |
| path=NULL; |
| lnpath=-1; |
| } |
| |
| /* remove extension */ |
| for (c=f; *c && *c!='.'; c++); |
| if (*c) { |
| ext=c; |
| lnext=n-(ext-f); |
| } else { |
| ext=NULL; |
| lnext=-1; |
| } |
| n=c-f; |
| |
| if (path) printf("getting %s in %s\n",f,path); |
| else printf("getting %s\n",f); |
| |
| transfer_init(fileget); |
| |
| /* sending filename (can contain * and ?) */ |
| obuf[0]=VB_UNLOAD; |
| memcpy(&obuf[1],f,n); |
| osize=n+1; |
| otries=0; |
| |
| while(1) { |
| SEND(obuf,osize); |
| numpacket='1'; |
| RECV(); |
| if (ibuf[0]==VB_FILES_OVER[0]) break; /* end of file list */ |
| if (res<3) { showPacket(res); continue; } |
| if (ibuf[0]!=VB_FILEHERE) { showPacket(res); continue; } |
| if (ibuf[1]!=numpacket) { showPacket(res); continue; } |
| |
| /* ok, VisioBraille proposed a file, let's try to get it */ |
| if (res-3>VB_MAXFNLEN) { |
| fprintf(stderr,"name too long, giving up that file\n"); |
| obuf[0]=VB_NEXT; |
| obuf[1]=ibuf[1]; |
| osize=2; |
| otries=0; |
| continue; |
| } |
| /* copy its name */ |
| { |
| char fullpath[(lnpath+1)+(res-3)+(ext?lnext+1:strlen(visiobases_ext))+1]; |
| if (path) { |
| strcpy(fullpath,path); |
| strcat(fullpath,"/"); |
| } |
| for(c=ibuf+3,d=fullpath+lnpath+1;c-ibuf<res;*d++=tolower(*c++)) |
| fullpath[lnpath+1+res-3]='\0'; |
| strcat(fullpath,ext?ext:visiobases_ext); |
| |
| if (backup && stat(fullpath,&st)>=0) |
| if (renameBackups(fullpath)==-1) |
| perror("couldn't rename backups, overwriting"); |
| if ((fd=open(fullpath,O_WRONLY|O_CREAT|O_TRUNC,0644))<0) { |
| /* openout failed, give up that file */ |
| perror(fullpath); |
| fprintf(stderr,"open failed, giving up that file\n"); |
| obuf[0]=VB_NEXT; |
| obuf[1]=ibuf[1]; |
| osize=2; |
| otries=0; |
| continue; |
| } |
| |
| /* start transfer : */ |
| obuf[0]=VB_ACK_DATA; |
| obuf[1]=numpacket; |
| osize=2; |
| otries=0; |
| numpacket=(numpacket+1)&'7'; |
| sizetransferred=0; |
| /* ready to transfer ! */ |
| while (1) { |
| SEND(obuf,osize); |
| printf("\r%s: %dKo...",fullpath,sizetransferred>>10); |
| fflush(stdout); |
| RECV(); |
| if (ibuf[0]==VB_DATA_OVER) break; |
| if (res<2) { showPacket(res); continue; } |
| if (ibuf[0]!=VB_HERES_DATA) { showPacket(res); continue; } |
| if (ibuf[1]!=numpacket) { showPacket(res); continue; } |
| if (write(fd,ibuf+2,res-2)<res-2) { |
| fprintf(stderr,"writing data on disk for file %s\n" |
| "So giving up\n",fullpath); |
| transfer_abort(RET_EUNIX); |
| } |
| obuf[0]=VB_ACK_DATA; |
| obuf[1]=numpacket; |
| osize=2; |
| otries=0; |
| sizetransferred+=res-2; |
| numpacket=(numpacket+1)&'7'; |
| } |
| /* transfer finished */ |
| close(fd); |
| printf("ok\n"); |
| obuf[0]=VB_OK; |
| osize=1; |
| otries=0; |
| } |
| } |
| transferring=0; |
| } |
| |
| |
| static int tryToFind(char *f, int *fd) { |
| if ((*fd=open(f,O_RDONLY))<0) { |
| strcpy(ibuf,f); |
| strcat(ibuf,visiobases_ext); |
| if ((*fd=open(ibuf,O_RDONLY))<0) { |
| strcpy(ibuf,f); |
| strcat(ibuf,".vis"); |
| if ((*fd=open(ibuf,O_RDONLY))<0) { |
| strcpy(ibuf,f); |
| strcat(ibuf,".Vis"); |
| if ((*fd=open(ibuf,O_RDONLY))<0) { |
| strcpy(ibuf,f); |
| strcat(ibuf,".VIS"); |
| if ((*fd=open(ibuf,O_RDONLY))<0) { |
| return 0; |
| } |
| } |
| } |
| } |
| } |
| return 1; |
| } |
| |
| /* fileput */ |
| /* send file f to VisioBraille */ |
| void fileput(char *f) { |
| int n; |
| char *c; |
| int res; |
| int fd; |
| |
| if (visiobases_dir && (f[0]!='.' || (f[1]!='.' && f[1]!='/') || (f[1]=='.' && f[2]!='/'))) { |
| char *f2 = malloc(strlen(visiobases_dir)+1+strlen(f)); |
| strcpy(f2,visiobases_dir); |
| strcat(f2,f); |
| if (tryToFind(f2, &fd)) { |
| free(f2); |
| goto ok; |
| } |
| fprintf(stderr,"couldn't get it from download directory, trying from current directory.\n"); |
| free(f2); |
| } |
| if (!tryToFind(f, &fd)) { |
| fprintf(stderr,"open failed, giving up that file\n"); |
| return; |
| } |
| |
| ok: |
| transfer_init(fileput); |
| |
| printf("putting %s\n",f); |
| |
| /* truncate filename : no extension, 8 chars max */ |
| if ((c=strrchr(f,'/'))) f=c+1; |
| for (c=f;*c && *c!='.' && c<f+VB_MAXFNLEN;c++); |
| n=c-f; |
| |
| obuf[0]=VB_FILEHERE; |
| obuf[1]=numpacket='1'; |
| obuf[2]=VB_FILET_AGENDA; |
| memcpy(&obuf[3],f,n); |
| memcpy(filename,f,n); |
| filename[n]=0; |
| osize=n+3; |
| otries=0; |
| |
| sizetransferred=0; |
| while(1) { |
| SEND(obuf,osize); |
| printf("\r%s: %dKo...",filename,sizetransferred>>10); |
| fflush(stdout); |
| RECV(); |
| if (ibuf[0]==VB_NEXT) break; |
| if (res<2) { showPacket(res); continue; } |
| if (ibuf[0]!=VB_ACK_DATA) { showPacket(res); continue; } |
| if (ibuf[1]!=numpacket) { showPacket(res); continue; } |
| if ((res=read(fd,&obuf[2],SIZE_PUT))<0) { |
| fprintf(stderr,"reading data on disk for file %s failed: %s\n" |
| "So giving up for this file\n",strerror(errno),filename); |
| break; |
| } |
| if (res==0) { /* eof */ |
| obuf[0]=VB_DATA_OVER; |
| osize=1; |
| otries=0; |
| WaitForOk(); |
| break; |
| } |
| obuf[0]=VB_HERES_DATA; |
| obuf[1]=numpacket=(numpacket+1)&'7'; |
| osize=res+2; |
| otries=0; |
| sizetransferred+=res; |
| } |
| /* transfer finished */ |
| close(fd); |
| printf("ok\n"); |
| } |
| |
| /* transfer_init */ |
| /* send the correct "I" packet, according to options */ |
| void transfer_init(transferfun *f) { |
| if (transferring) return; |
| transferring=1; |
| |
| obuf[0]=VB_INIT_PARAMS; |
| obuf[1]=(f==fileget?VB_UNLOAD:VB_LOAD); |
| obuf[2]=burstmode; |
| osize=3; |
| otries=0; |
| WaitForOk(); |
| numpacket='1'; |
| } |
| |
| /* transfer_finish */ |
| /* terminate transfer (also end of file list) */ |
| void transfer_finish(transferfun *f) { |
| if (!transferring) return; |
| transferring=0; |
| if (f==fileput) |
| SEND(VB_FILES_OVER,strlen(VB_FILES_OVER)); |
| } |
| |
| /* transfer_abort */ |
| /* if something nasty occured, try to clean */ |
| void transfer_abort(int exitnum) { |
| brlapi_sendRaw(VB_RESET,strlen(VB_RESET)); |
| brlapi_leaveRaw(); |
| brlapi_closeConnection(); |
| exit(exitnum); |
| } |
| |
| /* transfer_timeout */ |
| /* called when RecvPacket timed out */ |
| void transfer_timeout(int signum) { |
| if (otries++>=NUM_TRIES) { |
| fprintf(stderr,"No reply from terminal ! Assuming dead, hence aborting\n"); |
| transfer_abort(RET_EPROTO); |
| } |
| SEND(obuf,osize); |
| alarm(1); |
| } |