/******************************************************************** Copyright (C) 2001 Bassoukos Tassos 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. *********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "messages.h" #include "protocol.h" #include "tasks.h" enum { PERC_CHANGED, SENT, LAST_SIGNAL }; static guint message_signals[LAST_SIGNAL]={0,0}; static GtkObjectClass *parent_class=NULL; static void message_finalize_destroy_object(gpointer o,gpointer dummy){ object_destroy(o); } static void message_finalize(GtkObject *o){ Message *m; g_return_if_fail(o!=NULL); g_return_if_fail(IS_MESSAGE(o)); m=MESSAGE(o); g_list_foreach(m->objects,message_finalize_destroy_object,NULL); g_list_free(m->objects); m->objects=NULL; if(m->task) { gtk_object_unref(GTK_OBJECT(m->task)); } parent_class->finalize(o); } typedef void (*Signal_NONE__FLOAT) (GtkObject * object, gfloat arg2, gpointer user_data); static void message_marshal_change_signal(GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *args) { Signal_NONE__FLOAT rfunc=(Signal_NONE__FLOAT)func; (*rfunc)(object,GTK_VALUE_FLOAT(args[0]),func_data); } static void message_klass_init(MessageClass *klass){ GtkObjectClass *object_class=(GtkObjectClass*)klass; parent_class=gtk_type_class(GTK_TYPE_OBJECT); object_class->finalize=message_finalize; message_signals[PERC_CHANGED]= gtk_signal_new("percentage-changed", GTK_RUN_LAST|GTK_RUN_NO_RECURSE, object_class->type, GTK_SIGNAL_OFFSET(MessageClass,perc_changed), message_marshal_change_signal, GTK_TYPE_NONE,1,GTK_TYPE_FLOAT); message_signals[SENT]= gtk_signal_new("sent", GTK_RUN_LAST|GTK_RUN_NO_RECURSE, object_class->type, GTK_SIGNAL_OFFSET(MessageClass,sent), gtk_marshal_NONE__NONE, GTK_TYPE_NONE,0); gtk_object_class_add_signals(object_class,message_signals,LAST_SIGNAL); klass->perc_changed=NULL; klass->sent=NULL; } static void message_init(Message *m){ m->objects=NULL; m->c=NULL; m->start_percent=0.0; m->end_percent=1.0; m->is_finished=FALSE; memset(&m->w,0,sizeof(m->w)); } GtkType message_get_type(void){ static GtkType message_type=0; if(message_type==0){ static const GtkTypeInfo type_info={ "Message", sizeof(Message), sizeof(MessageClass), (GtkClassInitFunc) message_klass_init, (GtkObjectInitFunc) message_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; message_type=gtk_type_unique(GTK_TYPE_OBJECT,&type_info); gtk_type_set_chunk_alloc(message_type,8); } return message_type; } /******************************************/ Message *message_new(Connection *c,int message_type){ static int counter=1; Message *m; g_return_val_if_fail(c!=NULL,NULL); m=(Message *)gtk_type_new(MESSAGE_TYPE); m->c=c; m->w.task=counter++; m->w.type=message_type; return m; } int message_get_tasknum(Message *m){ g_return_val_if_fail(IS_MESSAGE(m),0); return m->w.task; } void message_set_pos(Message *m,gfloat pos){ g_return_if_fail(IS_MESSAGE(m)); pos=m->start_percent+pos*(m->end_percent-m->start_percent); if(m->curpos==pos) return; m->curpos=pos; gtk_signal_emit(GTK_OBJECT(m),message_signals[PERC_CHANGED],m->curpos); } void message_add_object(Message *m, HLObject *o){ g_return_if_fail(IS_MESSAGE(m)); m->objects=g_list_prepend(m->objects,o); } static gboolean mfo_check_object(HLObject *object,int *type){ return (object->type==*type)?FALSE:TRUE; } HLObject *message_find_object(Message *m, int object_type){ GList *l; g_return_val_if_fail(m!=NULL,NULL); g_return_val_if_fail(IS_MESSAGE(m),NULL); l=g_list_find_custom(m->objects,&object_type,(GCompareFunc)mfo_check_object); if(l==NULL) return NULL; return l->data; } struct tade { GList *list_in; GList *list_out; int type; }; static void meo_foreach(HLObject *o,struct tade *t){ GList **l; if(o->type==t->type) l=&t->list_in; else l=&t->list_out; *l=g_list_prepend(*l,o); } static void meo_foreach_matched(HLObject *o,HLObject ***array){ *(*array)++=o; } HLObject **message_extract_objects(Message *m, int object_type, int *count){ HLObject **ob=NULL,**bob; struct tade t; int i; g_return_val_if_fail(m!=NULL,NULL); g_return_val_if_fail(IS_MESSAGE(m),NULL); t.list_in=NULL; t.list_out=NULL; t.type=object_type; g_list_foreach(m->objects,(GFunc)meo_foreach,&t); /* the internal representation of the objects is in reverse order e.g. the first object in the list is actually the last received. The g_list_foreach that just happened split the list in two, depending on the object types, but both lists are now in the original order, e.g. the first object in the list is the first object received. Therefore, we need to reverse the list of objects we want to keep. Some servers depend on the object order. */ t.list_out=g_list_reverse(t.list_out); g_list_free(m->objects); m->objects=t.list_out; m->w.object_count=g_list_length(m->objects); i=g_list_length(t.list_in); *count=i; if(i>0){ ob=malloc(sizeof(HLObject *)*(i+1)); bob=ob; g_list_foreach(t.list_in,(GFunc)meo_foreach_matched,&bob); *bob=NULL; } g_list_free(t.list_in); return ob; } static void message_update_task(Message *m,gpointer dummy){ task_async_set_printf(m->task,m->curpos,NULL); } void message_set_task(Message *m,Task *t,gfloat start_pos,gfloat end_pos){ g_return_if_fail(IS_MESSAGE(m)); if(m->task!=NULL){ gtk_signal_disconnect_by_data(GTK_OBJECT(m),m->task); gtk_object_unref(GTK_OBJECT(m->task)); } m->task=t; if(m->task==NULL) return; g_return_if_fail(IS_TASK(t)); gtk_object_ref(GTK_OBJECT(m->task)); gtk_signal_connect(GTK_OBJECT(m),"percentage-changed",GTK_SIGNAL_FUNC(message_update_task),m->task); m->start_percent=start_pos; m->end_percent=end_pos; m->curpos=0; } gboolean message_is_error(Message *m){ g_return_val_if_fail(IS_MESSAGE(m),FALSE); return (m->w.error)?TRUE:FALSE; } gboolean message_is_reply(Message *m){ g_return_val_if_fail(IS_MESSAGE(m),FALSE); return (m->w.info)?TRUE:FALSE; } gboolean message_is_complete(Message *m){ g_return_val_if_fail(IS_MESSAGE(m),FALSE); return m->is_finished; } /* The following function should only be called from their respective threads. */ struct ObjectInfo { ObjectDef *od; unsigned char *buffer; int size; }; static void ms_serialize_object(HLObject *o,GList **l){ struct ObjectInfo *oi; oi=malloc(sizeof(struct ObjectInfo)); oi->od=find_object_type(o->type); oi->size=oi->od->write(o,&oi->buffer,oi->od->data); *l=g_list_prepend(*l,oi); } static void ms_count_sizes(struct ObjectInfo *oi,int *size){ *size+=oi->size; } static void ms_free_info(struct ObjectInfo *oi,void *dummy){ free(oi->buffer); free(oi); } void message_send(Message *m){ WireMessage w; struct { guint16 type; guint16 size; } obj_header; GList *oinfo=NULL,*i; struct ObjectInfo *hlo; char *buf; int total_bytes,current_bytes=0; w.object_count=g_list_length(m->objects); w.data_length=sizeof(w.object_count); g_list_foreach(m->objects,(GFunc)ms_serialize_object,&oinfo); g_list_foreach(oinfo,(GFunc)ms_count_sizes,&w.data_length); w.data_length+=sizeof(obj_header)*w.object_count; total_bytes=w.data_length+SIZE_OF_WIRETRANSACTION; buf=malloc(total_bytes); #define buf_write(size,ptr) do {memcpy(buf+current_bytes,ptr,size);current_bytes+=size;} while(0) #ifdef DEBUG printf("STrans type=%03d task=%d info=%d error=%d ocount=%d\n", m->w.type,m->w.task,m->w.info,m->w.error,w.object_count); #endif w.data_length=GUINT32_TO_BE(w.data_length); w.data_length_copy=w.data_length; w.info=GUINT16_TO_BE(m->w.info); w.type=GUINT16_TO_BE(m->w.type); w.task=GUINT32_TO_BE(m->w.task); w.error=GUINT32_TO_BE(m->w.error); w.object_count=GUINT16_TO_BE(w.object_count); buf_write(SIZE_OF_WIRETRANSACTION+sizeof(w.object_count),&w); for(i=oinfo;i!=NULL;i=g_list_next(i)){ hlo=i->data; #ifdef DEBUG printf(" %03d %d\n",hlo->od->type,hlo->size); #endif obj_header.type=GUINT16_TO_BE(hlo->od->type); obj_header.size=GUINT16_TO_BE(hlo->size); buf_write(sizeof(obj_header),&obj_header); buf_write(hlo->size,hlo->buffer); } g_list_foreach(oinfo,(GFunc)ms_free_info,NULL); g_list_free(oinfo); g_return_if_fail(total_bytes==current_bytes); connection_enq_send_buffer(m->c,buf,total_bytes,m); } gboolean message_recv_objects(Message *m){ char *bufptr=m->c->rcvbuffer; guint16 type,size; int i; HLObject *ob; g_return_val_if_fail(IS_MESSAGE(m),FALSE); m->is_finished=TRUE; if(m->w.data_length!=0){ memcpy(&m->w.object_count,bufptr,sizeof(guint16)); bufptr+=sizeof(guint16); m->w.object_count=GUINT16_FROM_BE(m->w.object_count); } #ifdef DEBUG printf("RTrans type=%03d task=%d info=%d error=%d ocount=%d\n", m->w.type,m->w.task,m->w.info,m->w.error,m->w.object_count); #endif if(m->w.object_count==0) return TRUE; for(i=0;iw.object_count;i++){ memcpy(&type,bufptr,sizeof(guint16)); bufptr+=sizeof(guint16); memcpy(&size,bufptr,sizeof(guint16)); bufptr+=sizeof(guint16); type=GUINT16_FROM_BE(type); size=GUINT16_FROM_BE(size); #ifdef DEBUG printf(" %03d %d",type,size); #endif ob=read_object(type,size,bufptr); bufptr+=size; #ifdef DEBUG printf("\n"); #endif if(ob!=NULL) { m->objects=g_list_prepend(m->objects,ob); } else { m->w.object_count--; i--; } } return TRUE; } /* Read from a connection the header of a message returns NULL on failure <-- close connection */ Message *message_new_from_connection(Connection *c){ Message *m; WireMessage *w; g_return_val_if_fail(c!=NULL,NULL); m=gtk_type_new(MESSAGE_TYPE); m->is_finished=FALSE; m->c=c; w=&(m->w); memcpy(w,c->rcvbuffer,SIZE_OF_WIRETRANSACTION); w->object_count=0; w->info=GUINT16_FROM_BE(w->info); w->type=GUINT16_FROM_BE(w->type); w->task=GUINT32_FROM_BE(w->task); w->error=GUINT32_FROM_BE(w->error); w->data_length=GUINT32_FROM_BE(w->data_length); w->data_length_copy=GUINT32_FROM_BE(w->data_length_copy); return m; } /* ======================================================= */ void message_fire_and_forget(Message *m) { g_return_if_fail(m!=NULL); g_return_if_fail(IS_MESSAGE(m)); message_send(m); message_unref(m); }