/* ** Amster - Upload ** by Jacob Laursen */ #include "include/config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "include/mui.h" #include #include "include/gui.h" #include "include/panel.h" #include "include/prefs.h" #include "include/share.h" #include "include/transfer.h" #include "include/upload.h" #include "amster_Cat.h" #include "include/protos.h" #define ULC_STATE 0 #define ULC_SIZE 1 int ul_count = 0; ULONG ul_new(struct IClass *cl, Object *obj, struct opSet *msg); void ul_cps(struct TransferData *data); MUIF ul_dispatch(REG(a0) struct IClass *cl,REG(a2) Object *obj,REG(a1) Msg msg) { struct TransferData *data = INST_DATA(cl,obj); switch(msg->MethodID) { case OM_NEW: return(ul_new(cl,obj,(APTR)msg)); case MUIM_Window_Setup: return(dl_setup(cl,obj,(APTR)msg)); case MUIM_Window_Cleanup: return(dl_muicleanup(cl,obj,(APTR)msg)); case UPLOAD_OPEN: set(obj, MUIA_Window_Open, TRUE); return(NULL); case UPLOAD_CLOSE: set(obj, MUIA_Window_Open, FALSE); return(NULL); case UPLOAD_UPDATE: { long pos = MUIV_NList_GetPos_Start; DoMethod(data->list,MUIM_NList_GetPos,(((muimsg)msg)->arg1),&pos); DoMethod(data->list,MUIM_NList_Redraw,pos); return(NULL); } case UPLOAD_CPS: ul_cps(data); return(NULL); case UPLOAD_ADD: DoMethod(data->list,MUIM_NList_InsertSingle,(songtrans)(((muimsg)msg)->arg1),MUIV_NList_Insert_Bottom); return(NULL); case UPLOAD_START: ul_startq2(data, (char *)(((muimsg)msg)->arg1), (char *)(((muimsg)msg)->arg2), (u_long)(((muimsg)msg)->arg3), (int)(((muimsg)msg)->arg4)); return(NULL); case UPLOAD_INFO: TransferInfo(data); return(0); case UPLOAD_ABORT: ul_abort(data); return(NULL); case UPLOAD_CLEANUP: TransferCleanup(data); return(NULL); } return(DoSuperMethodA(cl,obj,msg)); } ULONG ul_new(struct IClass *cl, Object *obj, struct opSet *msg) { static struct Hook uplistdispHook = { {0,0}, &translistdisp, NULL, NULL }; struct TransferData *data; Object *list, *info, *BT_Abort, *BT_Clean; if (obj = (Object *)DoSuperNew(cl,obj, MUIA_Window_Title, MSG_UPLOAD_TITLE, MUIA_Window_ID, MAKE_ID('U','P','L','D'), WindowContents, VGroup, Child, list = NListviewObject, MUIA_NListview_NList, NListObject, InputListFrame, MUIA_Font, MUIV_Font_Tiny, MUIA_NList_Title, TRUE, MUIA_NList_Format, "BAR, BAR, BAR, BAR, BAR", MUIA_NList_DisplayHook, &uplistdispHook, MUIA_CycleChain, 1, End, End, Child, info = TextObject, TextFrame, MUIA_Background, MUII_TextBack, MUIA_Text_PreParse, "\33c", End, Child, HGroup, Child, BT_Abort = SimpleButton(MSG_UPLOAD_ABORT_GAD), Child, BT_Clean = SimpleButton(MSG_UPLOAD_CLEANUP_GAD), End, End, TAG_MORE, msg->ops_AttrList)) { data = INST_DATA(cl,obj); data->list = list; data->info = info; data->ihnode.ihn_Object = obj; data->ihnode.ihn_Millis = 1000; data->ihnode.ihn_Method = UPLOAD_CPS; data->ihnode.ihn_Flags = MUIIHNF_TIMER; DoMethod(BT_Abort, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, UPLOAD_ABORT ); DoMethod(BT_Clean, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, UPLOAD_CLEANUP); DoMethod(list, MUIM_Notify, MUIA_NList_EntryClick, MUIV_EveryTime, obj, 1, UPLOAD_INFO); DoMethod(obj, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, obj, 1, UPLOAD_CLOSE); return((ULONG)obj); } return(0); } void ul_cps(struct TransferData *data) { songtrans sd; u_long item; int i; for (i=0; ; i++) { DoMethod(data->list, MUIM_NList_GetEntry, i, &item); if (!item) return; sd = (songtrans)item; if (sd->state == DLS_UP) { CalculateCps(sd); DoMethod(gui->uwin, UPLOAD_UPDATE, sd); } } } void ul_addq(song s) /* This is the initial function, called from share.c */ { songtrans sd; char *path; gui_debugf("ul_addq() - upload count: %d, queue limit: %d", ul_count, prf->UploadQueueLimit); path = MakeWinPath(s->title); if (ul_count < prf->UploadQueueLimit) { sprintf(nap_buf, "%s \"%s\"", s->user, path); free(path); nap_send(NAPC_UPLOADACCEPT); } else { sprintf(nap_buf, "%s \"%s\" %d", s->user, path, prf->UploadQueueLimit); free(path); nap_send(NAPC_LOCALQUEUEFULL); gui_debugf("Queue limit reached! (%d)", prf->UploadQueueLimit); return; } sd = malloc(sizeof(_songtrans)); if (!sd) return; memset(sd, 0, sizeof(_songtrans)); sd->song = nap_songdup(s); if (!sd->song) { free(sd); return; } sd->size = s->size; sd->mynick = prf->user; sd->type = TYPE_UPLOAD_OUT; DoMethod(gui->uwin, UPLOAD_ADD, sd); } void ul_startq(char *title, char *user, u_long ip, int port, int link) /* This is the initial function, when the actual upload is about to take place (acknowledged by server) - called from napster.c */ { DoMethod(gui->uwin, UPLOAD_START, title, user, ip, port); } void ul_startq2(struct TransferData *data, char *title, char *user, u_long ip, int port) /* ul_startq() continued (to get TransferData struct) */ { u_long tmp; songtrans sd; long i; char *path; for (i=0; ; i++) { DoMethod(data->list, MUIM_NList_GetEntry, i, &tmp); if (!tmp) return; sd = (songtrans)tmp; path = MakeWinPath(sd->song->title); if (sd->state==DLS_PREP && stricmp(user, sd->song->user)==0 && strcmp(title, path)==0) break; } free(path); if (sd->t) return; /* Already in a thread */ gui_debugf("ul-ack [%s], port: %d, ip: %ld\n",title,port,ip); /* We haven't filled out sd->song completely yet, since not much information was available when the request was made */ sd->song->ip = ip; /* Why do we need the IP twice? Check and use only one of them! */ sd->ip = ip; sd->port = port; sd->t = th_spawn(ul_handlemsg, "Amster uploader", UploadThread, prf->UploadTaskPri, sd); if (!sd->t) { sd->state = DLS_ERROR; DoMethod(gui->uwin, DL_UPDATE, sd); } } void ul_abort(struct TransferData *data) { u_long item; songtrans sd; DoMethod(data->list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &item); if (!item) return; sd = (songtrans)item; if (!sd->t) { sd->state = DLS_ABORT; DoMethod(gui->uwin, UPLOAD_UPDATE, sd); return; } else { if (sd->ts) th_message(sd->t, THC_EXIT, 0); } } void ul_handlemsg(thread t, int com, APTR data) { songtrans sd=(songtrans)t->data; switch(com) { case THC_STARTUP: gui_debugf("upload thread start"); sd->ts = 1; ul_count++; nap_sendbuf(NAPC_ULINC, ""); break; case THC_EXIT: gui_debugf("upload thread exit = %ld", data); sd->error = (int)data; TransferHandleError(sd); sd->ts = 0; sd->t = NULL; ul_count--; nap_sendbuf(NAPC_ULCOMPLETE, ""); break; case DLC_STATE: sd->state = (int)data; case DLC_UPDATE: DoMethod(gui->uwin, UPLOAD_UPDATE, sd); break; default: gui_debugf("upload thread c: %d, d: %ld", com, data); } } /* Upload thread */ __asm __saveds void UploadThread(void) { thread t; songtrans sd; struct Library *DosBase; struct Library *SocketBase; char *buffer, *path; long tmp; long s; thmsg m; int len; t = thr_init(); if (!t) return; sd = t->data; if (!InitTransferThread(t, sd)) return; s = sd->s; buffer = sd->buffer; DosBase = sd->DosBase; SocketBase = sd->SocketBase; while (1) { u_long sigs; sigs = Wait(sd->nsigm|sd->msigm); if (sigs&(sd->msigm)) { m = (thmsg)GetMsg(t->port); if(m) { if (m->com == THC_EXIT) { thr_message(t, DLC_STATE, (APTR)DLS_ABORT); ExitTransferThread(sd, 0); return; } if (!m->isreply) { m->isreply=1; ReplyMsg((struct Message *)m); } } } if (sigs&(sd->nsigm)) { FD_ZERO(&sd->fds); FD_SET(s,&sd->fds); sd->tv.tv_sec = 0; sd->tv.tv_usec = 0; switch(sd->state) { case DLS_CON: if (WaitSelect(s+1,NULL,&sd->fds,NULL,&sd->tv,0) != 1) break; { sd->state = DLS_INIT; tmp = 0; IoctlSocket(s, FIONBIO, (char*)&tmp); /* Disable non-blocking I/O to the socket */ } case DLS_INIT: if (WaitSelect(s+1,&sd->fds,NULL,NULL,&sd->tv,0) != 1) break; { tmp = recv(s, buffer, 1, 0); if (tmp != 1 || (tmp == 1 && buffer[0] != '1')) { ExitTransferThread(sd, 30); return; } thr_message(t, DLC_UPDATE, 0); send(s, "SEND", 4, 0); path = MakeWinPath(sd->song->title); sprintf(buffer, "%s \"%s\" %ld", sd->mynick, path, sd->song->size); free(path); send(s, buffer, strlen(buffer),0); tmp = recv(s, buffer, 32, 0); if (tmp < 1) { sd->ErrorCode = Errno(); ExitTransferThread(sd, ERROR_NET); return; } buffer[tmp] = '\0'; sd->cur = atoi(buffer); if (sd->cur == 0 && buffer[0] != '0') { gui_debugf("%s\n", buffer); ExitTransferThread(sd, 31); return; } sd->f = Open(sd->song->title, MODE_OLDFILE); if (!sd->f) { sd->ErrorCode = IoErr(); ExitTransferThread(sd, ERROR_FILEOPEN); return; } Seek(sd->f, sd->cur, OFFSET_BEGINNING); sd->state = DLS_UP; sd->starttime = time(NULL); sd->resumestart = sd->cur; thr_message(t, DLC_UPDATE, 0); } case DLS_UP: if (WaitSelect(s+1,NULL,&sd->fds,NULL,&sd->tv,0) != 1) break; while (sd->cur < sd->size) { len = Read(sd->f, buffer, 4096); if (len == -1) { /* Error */ sd->ErrorCode = IoErr(); ExitTransferThread(sd, ERROR_FILEREAD); return; } if (len != 0) { tmp = send(s, buffer, len, 0); if (tmp == -1) { sd->ErrorCode = Errno(); ExitTransferThread(sd, ERROR_NET); return; } if (tmp != len) { gui_debugf("WARNING (please notify Laursen): all data from Read() was not send() - %ld/%ld.\n", len, tmp); } sd->cur += len; } if (len < 4096 && sd->cur < sd->size) { /* Read buffer wasn't filled and file is not finished */ thr_message(t, DLC_STATE, (APTR)DLS_ERROR); ExitTransferThread(sd, 33); return; } m = (thmsg)GetMsg(t->port); if (m) { if (m->com == THC_EXIT) { thr_message(t, DLC_STATE, (APTR)DLS_ABORT); ExitTransferThread(sd, 0); return; } if (!m->isreply) { m->isreply=1; ReplyMsg((struct Message *)m); } } FD_ZERO(&sd->fds); FD_SET(s,&sd->fds); } thr_message(t, DLC_STATE, (APTR)DLS_FIN); ExitTransferThread(sd, 0); return; } } } ExitTransferThread(sd, 0); }