extras; samples: Fixed warnings
[sdk] / samples / net / eCom / connection.ec
1 import "ecere"
2 import "mainPanel"
3
4 class MainPanel;
5 class ConnectionSocket;
6
7 define ECOMMUNICATOR_PROTOCOL_VERSION = 1;
8
9 enum PacketType { Connect = 1, Send, Receive, Message, Name, Data, DataAck };
10 enum Status { Ok, Done, Abort, OutOfSpace };
11
12 struct eComPacket
13 {
14    PacketType type;
15    uint dataSize;
16    union
17    {
18       struct
19       {
20          uint version;
21       } connect;
22       struct
23       {
24          FileSize fileSize;
25          // File Name (dataSize BYTEs)
26       } send;
27       struct
28       {
29          uint position;
30       } recv;
31       struct
32       {
33          Status status;
34       } ack;
35       struct
36       {
37          Status status;
38          // File Data (dataSize BYTEs)
39       } data;
40       struct
41       {
42          bool over;
43          // Message Data (dataSize BYTEs)
44       } msg;
45       // Name : Name (dataSize BYTEs)
46    };
47 };
48
49 // define DATA_BLOCK_SIZE = 4096;
50 define DATA_BLOCK_SIZE = 65536;
51
52 class Connection : Window
53 {
54    borderStyle = sizable, hasClose = true, hasMinimize = true, showInTaskBar = true, size = Size { 580, 600 },
55    minClientSize = Size { 300, 440 }, tabCycle = true, background = activeBorder;
56
57    Button close
58    {
59       this, text = "Close", size = Size { 80, 0 }, anchor = Anchor { right = 10, bottom = 5 }, hotKey = altC;
60       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
61       {
62          Destroy(0);
63          return true;
64       }
65    };
66    Label { this, text = "[=== UPLOAD ===]", position = Point { 10, 10 } };
67    EditBox sendFile
68    {
69       this, textHorzScroll = true, text = "Send File", size.h = 20, anchor = Anchor { left = 10, top = 40, right = 190 };
70
71       void NotifyUpdate(EditBox editBox)
72       {
73          send.disabled = fileSend || !FileExists(sendFile.line.text).isFile;
74       }
75
76    };
77    Button sendBrowse
78    {
79       this, text = "Browse", size = Size { 80,20 }, anchor = Anchor { right = 100, top = 40 }, hotKey = altB;
80
81       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
82       {
83          if(fileList.Modal() == ok)
84          {
85             sendFile.Clear();
86             sendFile.PutS(fileList.filePath);
87          }
88          return true;
89       }
90    };
91    Button send
92    {
93       this, text = "Send", size = Size { 80, 20 }, anchor = Anchor { right = 10, top = 40 }, hotKey = altS, disabled = true;
94       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
95       {
96          const char * fileName = sendFile.line.text;
97
98          fileSend = FileOpen(fileName, read);
99          if(fileSend)
100          {
101             char outputFile[MAX_FILENAME];
102             int size;
103             eComPacket * packet;
104             GetLastDirectory(fileName, outputFile);
105             size = strlen(outputFile);
106             packet = (eComPacket *)new byte[sizeof(eComPacket) + size];
107             strcpy(sendFileName, outputFile);
108             if(packet)
109             {
110                packet->type = Send;
111                packet->dataSize = size;
112                FileGetSize(fileName, &packet->send.fileSize);
113                CopyBytes(packet + 1, outputFile, size);
114
115                socket.Send(packet, sizeof(eComPacket) + packet->dataSize);
116
117                sendProgress.range = packet->send.fileSize;
118                sendProgress.progress = 0;
119
120                delete packet;
121             }
122
123             sending.Clear();
124             sending.Printf("Sending %s at", outputFile);
125          }
126          send.disabled = fileSend || !FileExists(sendFile.line.text).isFile;
127          sendCancel.disabled = fileSend ? false : true;
128          abortSend = false;
129          return true;
130       }
131    };
132    EditBox sending { this, textHorzScroll = true, readOnly = true, borderStyle = deep, inactive = true, size.h = 20, anchor = Anchor { left = 10, top = 70, right = 190 }, opacity = 0 };
133    Label sendSpeed { this, borderStyle = deep, anchor = Anchor { right = 100, top = 70 }, size = Size { 80, 20 } };
134    Button sendCancel
135    {
136       this, text = "Cancel", anchor = Anchor { right = 10, top = 70 }, size = Size { 80,20 }, disabled = true;
137
138       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
139       {
140          abortSend = true;
141          sendCancel.disabled = true;
142          return true;
143       }
144    };
145    ProgressBar sendProgress { this, borderStyle = deep, inactive = true, size.h = 20, anchor = Anchor { left = 10, top = 100, right = 10 } };
146    Label { this, text = "[=== DOWNLOAD ===]", position = Point { 10, 140 } };
147    EditBox receiving { this, textHorzScroll = true, readOnly = true, borderStyle = deep, inactive = true, anchor = Anchor { left = 10, top = 170, right = 190 }, size.h = 20, opacity = 0 };
148    Label recvSpeed { this, borderStyle = deep, anchor = Anchor { right = 100, top = 170 }, size = Size { 80, 20 } };
149    Button recvCancel
150    {
151       this, text = "Cancel", size = Size { 80, 20 }, anchor = Anchor { top = 168, right = 12 }, disabled = true;
152
153       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
154       {
155          abortRecv = true;
156          recvCancel.disabled = true;
157          return true;
158       }
159    };
160    ProgressBar recvProgress { this, borderStyle = deep, inactive = true, anchor = Anchor { left = 10, top = 200, right = 10 }, size.h = 20 };
161    EditBox log
162    {
163       this, readOnly = true, multiLine = true, autoEmpty = true, text = "Conversation", inactive = true, size = Size { 560, 226 }, anchor = Anchor { left = 10, top = 270, right = 10, bottom = 100 }, hasHorzScroll = true, hasVertScroll = true
164    };
165    Label { this, position = Point { 10, 250 }, labeledWindow = log };
166    Button clear
167    {
168       this, text = "Clear", anchor = Anchor { left = 10, bottom = 5 }, size = Size { 80, 0 }, hotKey = altR;
169
170       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
171       {
172          log.Clear();
173          return true;
174       }
175    };
176    Button backButton
177    {
178       this, text = "Background...", anchor = Anchor { bottom = 5 }, size = Size { 100, 0 }, hotKey = altK;
179
180       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
181       {
182          ColorPicker picker { hasClose = true, text = "Select a color/opacity value...", color = background };
183          picker.Modal();
184          background = picker.color;
185          return true;
186       }
187    };
188    EditBox message
189    {
190       this, multiLine = true, text = "Message", size = Size { 0, 50 }, anchor = Anchor { left = 10, right = 10, bottom = 32 }, disabled = true, hasHorzScroll = true, hasVertScroll = true, size.h = 50;
191
192       bool NotifyKeyDown(EditBox editBox, Key key, unichar ch)
193       {
194          if((SmartKey)key == ctrlEnter)
195          {
196             EditLine line;
197
198             log.End();
199             log.AddS("\n");
200             log.AddS("<");
201             log.AddS(localName);
202             log.AddS("> ");
203             for(line = message.firstLine; line; line = line.next)
204             {
205                eComPacket * packet;
206                const char * string = line.text;
207                int size = strlen(string);
208
209                packet = (eComPacket *)new byte[sizeof(eComPacket) + size];
210                if(packet)
211                {
212                   packet->type = Message;
213                   packet->dataSize = size;
214                   packet->msg.over = (line == message.lastLine);
215                   CopyBytes(packet + 1, string, size);
216
217                   socket.Send(packet, sizeof(eComPacket) + packet->dataSize);
218
219                   delete packet;
220                }
221                if(line != message.firstLine)
222                   log.AddS("\n");
223                log.AddS(string);
224             }
225             log.SetViewToCursor(false);
226             message.Clear();
227             return false;
228          }
229          return true;
230       }
231    };
232
233    ConnectionSocket socket;
234
235    FileDialog fileList
236    {
237       borderStyle = sizable, text = "Select a file to send...", size = Size { 300,400 }, master = this
238    };
239
240
241    // Messages
242    char * incomingMessage;
243    unsigned int incomingMsgLen;
244
245    // Sending
246    File fileSend;
247    Seconds sendTime;
248    bool abortSend;
249    char sendFileName[MAX_FILENAME];
250
251    // Receiving
252    File fileRecv;
253    Seconds recvTime;
254    bool abortRecv;
255    char recvFileName[MAX_FILENAME];
256
257    char * displayedName;
258    const char * localName;
259
260    void SendBlock(uint blockSize)
261    {
262       eComPacket * packet = (eComPacket *)new byte[sizeof(eComPacket) + blockSize];
263       uint size = abortSend ? 0 : fileSend.Read(packet + 1, 1, blockSize);
264       if(packet)
265       {
266          packet->type = Data;
267          packet->dataSize = size;
268          if(abortSend)
269             packet->data.status = Abort;
270          else
271             packet->data.status = (size < blockSize) ? Done : Ok;
272
273          sendProgress.progress += size;
274          sendTime = GetTime();
275          socket.Send(packet, sizeof(eComPacket) + packet->dataSize);
276          delete packet;
277       }
278    }
279
280    void SendName(const char * name)
281    {
282       eComPacket * namePacket = (eComPacket *)new byte[sizeof(eComPacket) + strlen(name)];
283       localName = name;
284       if(namePacket)
285       {
286          namePacket->type = Name;
287          namePacket->dataSize = strlen(name);
288
289          CopyBytes(namePacket + 1, name, namePacket->dataSize);
290
291          socket.Send(namePacket, sizeof(eComPacket) + namePacket->dataSize);
292
293          delete namePacket;
294       }
295    }
296
297    void OnDestroy()
298    {
299       ((MainPanel)master).ConnectionDestroyed(this);
300       if(socket)
301       {
302          socket.connection = null;
303          socket.Disconnect(0);
304          socket = null;
305       }
306       delete incomingMessage;
307       delete displayedName;
308       delete fileSend;
309       delete fileRecv;
310    }
311
312    bool OnCreate()
313    {
314       //eComPacket packet = { Connect, 0, { ECOMMUNICATOR_PROTOCOL_VERSION } };
315       eComPacket packet { Connect, 0, { ECOMMUNICATOR_PROTOCOL_VERSION } };
316       socket.Send(&packet, sizeof(eComPacket));
317       return true;
318    }
319
320    property ConnectionSocket socket { set { socket = value; } }
321 }
322
323 class ConnectionSocket : Socket
324 {
325    Connection connection;
326    MainPanel mainPanel;
327
328    uint OnReceive(const byte * buffer, uint count)
329    {
330       if(count >= sizeof(eComPacket))
331       {
332          eComPacket packet = *(eComPacket *) buffer;
333          if(count - sizeof(eComPacket) >= packet.dataSize)
334          {
335             byte * data = (byte *)buffer + sizeof(eComPacket);
336             if(connection)
337             {
338                switch(packet.type)
339                {
340                   case Connect:
341                   {
342                      if(packet.connect.version == ECOMMUNICATOR_PROTOCOL_VERSION)
343                      {
344                         char address[256] = "";
345                         connection.send.disabled = !FileExists(connection.sendFile.line.text).isFile;
346                         connection.message.disabled = false;
347                         GetNameFromAddress(inetAddress, address);
348                         connection.log.End();
349                         connection.log.Printf("* Connected to %s (%s), protocol version %d.", inetAddress, address, packet.connect.version);
350                      }
351                      else
352                         Disconnect(0);
353                      break;
354                   }
355                   case Send:
356                   {
357                      char fileName[MAX_LOCATION];
358                      FileSize resumePosition = 0;
359                      char string[MAX_LOCATION + 40];
360                      eComPacket recvPacket;
361
362                      CopyBytes(fileName, data, packet.dataSize);
363                      fileName[packet.dataSize] = '\0';
364
365                      if(FileExists(fileName))
366                      {
367                         FileGetSize(fileName, &resumePosition);
368                      }
369                      recvPacket.type = Receive;
370                      recvPacket.dataSize = 0;
371                      recvPacket.recv.position = resumePosition;
372
373                      strcpy(connection.recvFileName, fileName);
374                      connection.fileRecv = FileOpen(fileName, append);
375                      connection.recvCancel.disabled = connection.fileRecv ? false : true;
376
377                      connection.receiving.Clear();
378                      connection.receiving.Printf("Receiving %s at", fileName);
379
380                      connection.recvProgress.range = packet.send.fileSize;
381                      connection.recvProgress.progress = resumePosition;
382
383                      connection.log.End();
384                      if(resumePosition)
385                      {
386                         PrintSize(string, resumePosition, 2);
387                         connection.log.Printf("\n* Resuming download of %s from %s", connection.recvFileName, string);
388                      }
389                      else
390                         connection.log.Printf("\n* Downloading %s", connection.recvFileName);
391
392                      connection.abortRecv = false;
393
394                      connection.recvTime = GetTime();
395                      Send(&recvPacket, sizeof(eComPacket));
396                      break;
397                   }
398                   case Receive:
399                   {
400                      char string[20];
401                      connection.fileSend.Seek(packet.recv.position, start);
402                      connection.SendBlock(DATA_BLOCK_SIZE);
403                      connection.log.End();
404                      if(packet.recv.position)
405                      {
406                         PrintSize(string, packet.recv.position, 2);
407                         connection.log.Printf("\n* Resuming upload of %s from %s", connection.sendFileName, string);
408                      }
409                      else
410                         connection.log.Printf("\n* Uploading %s", connection.sendFileName);
411                      connection.sendProgress.progress = packet.recv.position;
412                      break;
413                   }
414                   case Message:
415                   {
416                      if(connection.incomingMsgLen)
417                         connection.incomingMessage[connection.incomingMsgLen++] = '\n';
418                      connection.incomingMessage = renew connection.incomingMessage char[connection.incomingMsgLen + packet.dataSize + 1];
419                      CopyBytes(connection.incomingMessage + connection.incomingMsgLen, data, packet.dataSize);
420                      connection.incomingMessage[connection.incomingMsgLen + packet.dataSize] = '\0';
421
422                      connection.incomingMsgLen += packet.dataSize;
423
424                      if(packet.msg.over)
425                      {
426                         connection.log.End();
427                         connection.log.AddS("\n<");
428                         connection.log.AddS(connection.displayedName);
429                         connection.log.AddS("> ");
430                         connection.log.AddS(connection.incomingMessage);
431                         connection.log.SetViewToCursor(false);
432                         delete connection.incomingMessage;
433                         connection.incomingMessage = null;
434                         connection.incomingMsgLen = 0;
435                      }
436                      break;
437                   }
438                   case Name:
439                   {
440                      char address[256] = "", title[256];
441
442                      delete connection.displayedName;
443                      connection.displayedName = new char[packet.dataSize + 1];
444                      CopyBytes(connection.displayedName, data, packet.dataSize);
445                      connection.displayedName[packet.dataSize] = '\0';
446
447                      GetNameFromAddress(inetAddress, address);
448                      sprintf(title, service ? "%s [<-%s]" : "%s [->%s]", connection.displayedName, address);
449
450                      connection.text = title;
451                      break;
452                   }
453                   case Data:
454                   {
455                      eComPacket ackPacket { DataAck };
456                      Seconds diffTime;
457
458                      if(connection.abortRecv)
459                         ackPacket.data.status = Abort;
460                      else if(packet.dataSize)
461                      {
462                         if(!connection.fileRecv.Write(data, packet.dataSize, 1))
463                            ackPacket.data.status = OutOfSpace;
464                      }
465                      else if(packet.data.status)
466                         ackPacket.data.status = packet.data.status;
467
468                      connection.recvProgress.progress += packet.dataSize;
469
470                      diffTime = GetTime() - connection.recvTime;
471                      if(diffTime)
472                      {
473                         char string[16];
474                         PrintSize(string, (int)(packet.dataSize / diffTime), 1);
475                         strcat(string, "/s");
476                         connection.recvSpeed.text = string;
477                      }
478
479                      if(ackPacket.data.status)
480                      {
481                         connection.log.End();
482                         switch(ackPacket.data.status)
483                         {
484                            case Done:
485                               connection.log.Printf("\n* %s received successfully.", connection.recvFileName);
486                               break;
487                            case Abort:
488                               connection.log.Printf("\n* Download of %s cancelled.", connection.recvFileName);
489                               break;
490                            case OutOfSpace:
491                               connection.log.Printf("\n* Out of disk space receiving %s.", connection.recvFileName);
492                               break;
493                         }
494                         connection.receiving.Clear();
495                         connection.recvSpeed.text = "";
496                         delete connection.fileRecv;
497                         connection.recvProgress.range = 0;
498                      }
499
500                      connection.recvCancel.disabled = connection.fileRecv ? false : true;
501                      connection.recvTime = GetTime();
502                      Send(&ackPacket, sizeof(eComPacket));
503                      break;
504                   }
505                   case DataAck:
506                   {
507                      Seconds diffTime;
508                      if(packet.ack.status)
509                      {
510                         connection.log.End();
511                         switch(packet.ack.status)
512                         {
513                            case Done:
514                               connection.log.Printf("\n* %s uploaded successfully.", connection.sendFileName);
515                               break;
516                            case Abort:
517                               connection.log.Printf("\n* Upload of %s cancelled.", connection.sendFileName);
518                               break;
519                            case OutOfSpace:
520                               connection.log.Printf("\n* Remote ran out of disk space receiving %s.", connection.sendFileName);
521                               break;
522                         }
523
524                         connection.sendProgress.range = 0;
525                         delete connection.fileSend;
526
527                         connection.sending.Clear();
528                         connection.sendSpeed.text = "";
529
530                         connection.send.disabled = connection.fileSend || !FileExists(connection.sendFile.line.text).isFile;
531                         connection.sendCancel.disabled = connection.fileSend ? false : true;
532                      }
533                      else
534                      {
535                         diffTime = GetTime() - connection.sendTime;
536                         if(diffTime)
537                         {
538                            char string[16];
539                            PrintSize(string, (int)(DATA_BLOCK_SIZE / diffTime), 1);
540                            strcat(string, "/s");
541                            connection.sendSpeed.text = string;
542                         }
543                         connection.SendBlock(DATA_BLOCK_SIZE);
544                      }
545                      break;
546                   }
547                }
548             }
549             return sizeof(eComPacket) + packet.dataSize;
550          }
551       }
552       return 0;
553    }
554
555    void OnConnect()
556    {
557       char address[256] = "";
558
559       GetNameFromAddress(inetAddress, address);
560
561       connection = Connection { text = "Connecting...", socket = this, master = mainPanel };
562       connection.Create();
563       mainPanel.OnConnect(connection, inetAddress, address);
564    }
565
566    void OnDisconnect(int code)
567    {
568       if(connection)
569       {
570          connection.socket = null;
571          connection.Destroy(0);
572          connection = null;
573       }
574    }
575
576    property MainPanel mainPanel { set { mainPanel = value; } }
577    property Connection connection { set { connection = value; } }
578 }