forest-net
an overlay networks for large-scale virtual worlds
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
ClientMgr-sav.cpp
1 
9 #include "ClientMgr.h"
10 #include <map>
11 #include <iostream>
12 #include <fstream>
13 #include <sstream>
14 
15 using namespace forest;
16 
26 int main(int argc, char *argv[]) {
27  ipa_t nmIp, myIp; uint32_t finTime = 0;
28 
29  if(argc != 4 ||
30  (nmIp = Np4d::ipAddress(argv[1])) == 0 ||
31  (myIp = Np4d::ipAddress(argv[2])) == 0 ||
32  (sscanf(argv[3],"%d", &finTime)) != 1)
33  fatal("usage: ClientMgr nmIp myIp fintime");
34 
35  if (!init(nmIp, myIp))
36  fatal("init: Failed to initialize ClientMgr");
37 
38  sub->run(finTime);
39 
40  return 0;
41 }
42 
43 namespace forest {
44 
50 bool init(ipa_t nmIp1, ipa_t myIp1) {
51  nmIp = nmIp1; myIp = myIp1;
52 
53  ps = new PacketStoreTs(10000);
54  cliTbl = new ClientTable(10000,30000,"clientData");
55  logger = new Logger();
56 
57  if (!cliTbl->init()) {
58  logger->log("ClientMgr::init: could not initialize "
59  "client data",2);
60  return false;
61  }
62 
63  uint64_t nonce;
64  if (!bootMe(nmIp, myIp, nmAdr, myAdr, rtrAdr, rtrIp, rtrPort, nonce)) {
65  return false;
66  }
67 
68  sub = new Substrate(myAdr, myIp, rtrAdr, rtrIp, rtrPort, nonce,
70  if (!sub->init()) return false;
71  sub->setRtrReady(true);
72 
73  dummyRecord = 0; maxRecord = 0;
74 
75  // read clientData file
76  clientFile.open("clientData");
77  if (!clientFile.good() || !cliTbl->read(clientFile)) {
78  logger->log("ClientMgr::init: could not read clientData "
79  "file",2);
80  return false;
81  }
82  clientFile.clear();
83 
84  // if size of file is not equal to count*RECORD_SIZE
85  // re-write the file using padded records
86  int n = cliTbl->getMaxClx();
87  clientFile.seekp(0,ios_base::end);
88  long len = clientFile.tellp();
89  if (len != (n+1)*RECORD_SIZE) {
90  for (int clx = 0; clx <= n; clx++) writeRecord(clx);
91  }
92  if (pthread_mutex_init(&clientFileLock,NULL) != 0) {
93  logger->log("ClientMgr::init: could not initialize lock "
94  "on client data file",2);
95  return false;
96  }
97 
98  acctFile.open("acctRecords",fstream::app);
99  if (!acctFile.good()) {
100  logger->log("ClientMgr::init: could not open acctRecords "
101  "file",2);
102  return false;
103  }
104  return true;
105 }
106 
107 bool bootMe(ipa_t nmIp, ipa_t myIp, fAdr_t& nmAdr, fAdr_t& myAdr,
108  fAdr_t& rtrAdr, ipa_t& rtrIp, ipp_t& rtrPort, uint64_t& nonce) {
109 
110  // open boot socket
111  int bootSock = Np4d::datagramSocket();
112  if (bootSock < 0) return false;
113  if (!Np4d::bind4d(bootSock,myIp,0) || !Np4d::nonblock(bootSock)) {
114  close(bootSock);
115  return false;
116  }
117 
118  // setup boot leaf packet to net manager
119  Packet p; buffer_t buf1; p.buffer = &buf1;
120  CtlPkt cp(CtlPkt::BOOT_LEAF,CtlPkt::REQUEST,1,p.payload());
121  int plen = cp.pack();
122  if (plen == 0) { close(bootSock); return false; }
124  p.flags = 0; p.srcAdr = p.dstAdr = 0; p.comtree = Forest::NET_SIG_COMT;
125  p.pack();
126 
127  // setup reply packet
128  Packet reply; buffer_t buf2; reply.buffer = &buf2;
129  CtlPkt repCp;
130 
131  int resendTime = Misc::getTime();
132  ipa_t srcIp; ipp_t srcPort;
133  while (true) {
134  int now = Misc::getTime();
135  if (now > resendTime) {
136  if (Np4d::sendto4d(bootSock,(void *) p.buffer, p.length,
137  nmIp,Forest::NM_PORT) == -1) {
138  close(bootSock); return false;
139  }
140  resendTime += 1000000; // retry after 1 second
141  }
142  int nbytes = Np4d::recvfrom4d(bootSock,reply.buffer,1500,
143  srcIp, srcPort);
144  if (nbytes < 0) { usleep(100000); continue; }
145  reply.unpack();
146 
147  // do some checking
148  if (srcIp != nmIp || reply.type != Forest::NET_SIG) {
149  logger->log("unexpected response to boot request",
150  2,reply);
151  close(bootSock); return false;
152  }
153  repCp.reset(reply);
154  if (repCp.type != CtlPkt::CONFIG_LEAF ||
155  repCp.mode != CtlPkt::REQUEST) {
156  logger->log("unexpected response from NetMgr",2,reply);
157  close(bootSock); return false;
158  }
159 
160  // unpack data from packet
161  myAdr = repCp.adr1; rtrAdr = repCp.adr2;
162  rtrIp = repCp.ip1; rtrPort = repCp.port1;
163  nonce = repCp.nonce;
164  nmAdr = reply.srcAdr;
165 
166  // send positive reply
167  repCp.reset(CtlPkt::CONFIG_LEAF,CtlPkt::POS_REPLY,repCp.seqNum,
168  repCp.payload);
169  plen = repCp.pack();
170  if (plen == 0) { close(bootSock); return false; }
171  reply.length = Forest::OVERHEAD + plen;
172  reply.srcAdr = myAdr; reply.dstAdr = nmAdr;
173  reply.pack();
174  if (Np4d::sendto4d(bootSock,(void *) reply.buffer, reply.length,
175  nmIp,Forest::NM_PORT) == -1) {
176  close(bootSock); return false;
177  }
178  break; // proceed to next step
179  }
180  // we have the configuration information, now just wait for
181  // final ack
182  while (true) {
183  int now = Misc::getTime();
184  if (now > resendTime) {
185  if (Np4d::sendto4d(bootSock,(void *) p.buffer, p.length,
186  nmIp,Forest::NM_PORT) == -1) {
187  close(bootSock); return false;
188  }
189  resendTime += 1000000; // retry after 1 second
190  }
191  int nbytes = Np4d::recvfrom4d(bootSock,reply.buffer,1500,
192  srcIp, srcPort);
193  if (nbytes < 0) { usleep(100000); continue; }
194  reply.unpack();
195  if (srcIp != nmIp || reply.type != Forest::NET_SIG) {
196  logger->log("unexpected response to boot request",
197  2,reply);
198  close(bootSock); return false;
199  }
200  // do some checking
201  repCp.reset(reply);
202  if (repCp.type == CtlPkt::CONFIG_LEAF &&
203  repCp.mode == CtlPkt::REQUEST) {
204  // our reply must have been lost, send it again
205  repCp.reset(CtlPkt::CONFIG_LEAF,CtlPkt::POS_REPLY,
206  repCp.seqNum, repCp.payload);
207  plen = repCp.pack();
208  if (plen == 0) { close(bootSock); return false; }
209  reply.length = Forest::OVERHEAD + plen;
210  reply.srcAdr = myAdr; reply.dstAdr = nmAdr;
211  reply.pack();
212  if (Np4d::sendto4d(bootSock,(void *) reply.buffer,
213  reply.length,nmIp,Forest::NM_PORT)
214  == -1) {
215  close(bootSock); return false;
216  }
217  } else if (repCp.type != CtlPkt::BOOT_LEAF ||
218  repCp.mode != CtlPkt::POS_REPLY) {
219  logger->log("unexpected response from NetMgr",
220  2,reply);
221  close(bootSock); return false;
222  }
223  break; // success
224  }
225  close(bootSock); return true;
226 }
227 
245 void* handler(void *qp) {
246  Queue& inq = ((Substrate::QueuePair *) qp)->in;
247  Queue& outq = ((Substrate::QueuePair *) qp)->out;
248  CpHandler cph(&inq, &outq, myAdr, logger, ps);
249 
250  while (true) {
251  pktx px = inq.deq();
252  bool success = false;
253  if (px < 0) {
254  // in this case, p is really a negated socket number
255  // for a remote client
256  int sock = -px;
257  success = handleClient(sock, cph);
258  close(sock);
259  } else {
260  Packet& p = ps->getPacket(px);
261  CtlPkt cp(p);
262  switch (cp.type) {
263  case CtlPkt::CLIENT_CONNECT:
264  case CtlPkt::CLIENT_DISCONNECT:
265  success = handleConnDisc(px, cp, cph);
266  break;
267  default:
268  cph.errReply(px,cp,"invalid control packet "
269  "type for ClientMgr");
270  break;
271  }
272  if (!success) {
273  string s;
274  cerr << "handler: operation failed\n"
275  << p.toString(s);
276  }
277  }
278  ps->free(px); // release px now that we're done
279  outq.enq(0); // signal completion to main thread
280  }
281 }
282 
288 bool handleClient(int sock,CpHandler& cph) {
289  int clx; string clientName;
290  NetBuffer buf(sock,1024);
291 
292  if (!loginDialog(sock,buf,clientName)) return true;
293  userDialog(sock,cph,buf,clientName);
294  return true;
295 }
296 
303 bool loginDialog(int sock, NetBuffer& buf, string& clientName) {
304  string client, pwd, s0, s1, s2, s3;
305  int numFailures = 0;
306  if (!buf.readWord(s0) || s0 != "Forest-login-v1" ||
307  !buf.nextLine() || !buf.readAlphas(s1)) {
308  Np4d::sendString(sock,"misformatted login dialog\n"
309  "overAndOut\n");
310  return 0;
311  }
312  while (true) {
313  // process up to 3 login attempts
314  // expected form for login
315  // Forest-login-v1
316  // login: clientName
317  // password: clientPassword
318  // over
319  if (s1 == "login") {
320  // process remainder of login dialog
321  if (buf.verify(':') && buf.readAlphas(clientName) &&
322  buf.nextLine() &&
323  buf.readAlphas(s2) && s2 == "password" &&
324  buf.verify(':') && buf.readWord(pwd) &&
325  buf.nextLine() &&
326  buf.readLine(s3) && s3 == "over") {
327  int clx =cliTbl->getClient(clientName);
328  // locks clx
329  if (clx == 0) {
330  Np4d::sendString(sock,"login failed: "
331  "try again\nover\n");
332  } else if (cliTbl->checkPassword(clx,pwd)) {
333  cliTbl->releaseClient(clx);
334  Np4d::sendString(sock,"login successful"
335  "\nover\n");
336  return true;
337  } else {
338  cliTbl->releaseClient(clx);
339  Np4d::sendString(sock,"login failed, "
340  "try again\nover\n");
341  if (!buf.readAlphas(s1)) {
342  Np4d::sendString(sock,
343  "misformatted login "
344  "dialog\n"
345  "overAndOut\n");
346  return false;
347  }
348  }
349  }
350  } else if (s1 == "newAccount") {
351  // attempt to add account
352  if (buf.verify(':') && buf.readAlphas(clientName) &&
353  buf.nextLine() &&
354  buf.readAlphas(s2) && s2 == "password" &&
355  buf.verify(':') && buf.readWord(pwd) &&
356  buf.nextLine() &&
357  buf.readLine(s3) && s3 == "over") {
358  int clx = cliTbl->getClient(clientName);
359  //locks clx
360  if (clx != 0) {
361  Np4d::sendString(sock,"client name not "
362  "available: try again\n"
363  "over\n");
364  cliTbl->releaseClient(clx);
365  }
366  clx = cliTbl->addClient(clientName,pwd,
367  ClientTable::STANDARD);
368  if (clx != 0) {
369  writeRecord(clx);
370  cliTbl->releaseClient(clx);
371  Np4d::sendString(sock,"success\n"
372  "over\n");
373  return true;
374  } else {
375  Np4d::sendString(sock,"unable to add "
376  "client\nover\n");
377  if (!buf.readAlphas(s1)) {
378  Np4d::sendString(sock,
379  "misformatted login "
380  "dialog\n"
381  "overAndOut\n");
382  return false;
383  }
384  }
385  }
386  } else {
387  Np4d::sendString(sock,"misformatted login dialog\n"
388  "overAndOut\n");
389  return false;
390  }
391  numFailures++;
392  if (numFailures >= 3) {
393  Np4d::sendString(sock,"login failed: you're done\n"
394  "overAndOut\n");
395  return false;
396  }
397  }
398 }
399 
400 void userDialog(int sock, CpHandler& cph, NetBuffer& buf, string& clientName) {
401  string cmd, reply;
402  while (buf.readAlphas(cmd)) {
403  reply = "success";
404  if (cmd == "newSession") {
405  newSession(sock,cph,buf,clientName,reply);
406  } else if (cmd == "getProfile") {
407  getProfile(buf,clientName,reply);
408  } else if (cmd == "updateProfile") {
409  updateProfile(buf,clientName,reply);
410  } else if (cmd == "changePassword") {
411  changePassword(buf,clientName,reply);
412  } else if (cmd == "uploadPhoto") {
413  uploadPhoto(sock,buf,clientName,reply);
414  } else if (cmd == "getSessions") {
415  getSessions(buf,clientName,reply);
416  } else if (cmd == "cancelSession") {
417  cancelSessions(buf,clientName,reply);
418  } else if (cmd == "addComtree" && buf.nextLine()) {
419  addComtree(buf,clientName,reply);
420  } else if (cmd == "over" && buf.nextLine()) {
421  // ignore
422  } else if (cmd == "overAndOut" && buf.nextLine()) {
423  break;
424  } else {
425  reply = "unrecognized input";
426  }
427  if (sock == -1) break;
428 cerr << "sending reply: " << reply << endl;
429  reply += "\nover\n";
430  Np4d::sendString(sock,reply);
431  }
432 cerr << "terminating" << endl;
433 }
434 
435 bool newSession(int sock, CpHandler& cph, NetBuffer& buf,
436  string& clientName, string& reply) {
437  string s1;
438  RateSpec rs;
439  if (buf.verify(':')) {
440  if (!buf.readRspec(rs) || !buf.nextLine() ||
441  !buf.readLine(s1) || s1 != "over") {
442  reply = "unrecognized input";
443  return false;
444  }
445  } else {
446  if (!buf.nextLine() || !buf.readLine(s1) || s1 != "over") {
447  reply = "unrecognized input";
448  return false;
449  }
450  }
451  int clx = cliTbl->getClient(clientName);
452  if (clx == 0) {
453  reply = "cannot access client data(" + clientName + ")";
454  return false;
455  }
456  if (!rs.isSet()) rs = cliTbl->getDefRates(clx);
457  // make sure we have sufficient capacity
458  if (!rs.leq(cliTbl->getAvailRates(clx))) {
459  cliTbl->releaseClient(clx);
460  reply = "session rate exceeds available capacity";
461  return true;
462  }
463  // proceed with new session setup
464  CtlPkt repCp;
465  ipa_t clientIp = Np4d::getSockIp(sock);
466  pktx rpx = cph.newSession(nmAdr, clientIp, rs, repCp);
467  if (rpx == 0) {
468  cliTbl->releaseClient(clx);
469  reply = "cannot complete login: NetMgr never responded";
470  return false;
471  }
472  if (repCp.mode != CtlPkt::POS_REPLY) {
473  cliTbl->releaseClient(clx);
474  reply = "cannot complete login: NetMgr failed (" +
475  repCp.errMsg + ")";
476  ps->free(rpx);
477  return false;
478  }
479  int sess = cliTbl->addSession(repCp.adr1, repCp.adr2, clx);
480  if (sess == 0) {
481  cliTbl->releaseClient(clx);
482  reply = "cannot complete login: could not create "
483  "session record";
484  return false;
485  }
486  cliTbl->setState(sess, ClientTable::PENDING);
487 
488  // set session information
489  const string& cliName = cliTbl->getClientName(clx);
490  cliTbl->setClientIp(sess,clientIp);
491  cliTbl->setRouterAdr(sess,repCp.adr2);
492  cliTbl->setState(sess,ClientTable::PENDING);
494  cliTbl->getAvailRates(sess).subtract(rs);
495  cliTbl->releaseClient(clx);
496 
497  // output accounting record
498  writeAcctRecord(cliName, repCp.adr1, clientIp, repCp.adr2, NEWSESSION);
499 
500  // send information back to client
501  stringstream ss; string s;
502  ss << "yourAddress: " << Forest::fAdr2string(repCp.adr1,s) << endl;
503  ss << "yourRouter: (" << Np4d::ip2string(repCp.ip1,s)
504  << "," << repCp.port1 << ",";
505  ss << Forest::fAdr2string(repCp.adr2,s) << ")\n";
506  ss << "comtCtlAddress: " << Forest::fAdr2string(repCp.adr3,s) << endl;
507  ss << "connectNonce: " << repCp.nonce << "\noverAndOut\n";
508  reply = ss.str();
509 
510  ps->free(rpx);
511  return true;
512 }
513 
514 void getProfile(NetBuffer& buf, string& clientName, string& reply) {
515  string s, targetName;
516 cerr << "reading target name\n";
517  if (!buf.verify(':') || !buf.readAlphas(targetName) || !buf.nextLine()){
518  reply = "could not read target name"; return;
519  }
520 cerr << "clientName=" << clientName << " targetName=" << targetName << endl;
521  int clx = cliTbl->getClient(clientName);
522  if (clx == 0) {
523  reply = "cannot access client data(" + clientName + ")";
524  return;
525  }
526  int tclx;
527  if (targetName == clientName) {
528  tclx = clx;
529  } else {
530  tclx = cliTbl->getClient(targetName);
531  if (tclx == 0) {
532  cliTbl->releaseClient(clx);
533  reply = "no such target client"; return;
534  }
535  ClientTable::privileges priv = cliTbl->getPrivileges(clx);
536  if (priv != ClientTable::ADMIN && priv != ClientTable::ROOT) {
537  reply = "this operation requires administrative "
538  "privileges";
539  cliTbl->releaseClient(clx);
540  cliTbl->releaseClient(tclx);
541  return;
542  }
543  ClientTable::privileges tpriv = cliTbl->getPrivileges(tclx);
544  if (priv != ClientTable::ROOT && tpriv == ClientTable::ROOT) {
545  reply = "this operation requires root privileges";
546  cliTbl->releaseClient(clx);
547  cliTbl->releaseClient(tclx);
548  return;
549  }
550  }
551  reply = "realName: \"" + cliTbl->getRealName(tclx) + "\"\n";
552  reply += "email: " + cliTbl->getEmail(tclx) + "\n";
553  reply += "defRates: " + cliTbl->getDefRates(tclx).toString(s) + "\n";
554  reply += "totalRates: " + cliTbl->getTotalRates(tclx).toString(s) +"\n";
555  cliTbl->releaseClient(clx);
556  if (tclx != clx) cliTbl->releaseClient(tclx);
557 }
558 
559 void updateProfile(NetBuffer& buf, string& clientName, string& reply) {
560  string s, targetName;
561 cerr << "reading target name\n";
562  if (!buf.verify(':') || !buf.readAlphas(targetName) || !buf.nextLine()){
563  reply = "could not read target name"; return;
564  }
565  int clx = cliTbl->getClient(clientName);
566  if (clx == 0) {
567  reply = "cannot access client data(" + clientName + ")";
568  return;
569  }
570 cerr << "got (" << targetName << ") and I am (" << clientName << ")" << endl;
571  int tclx;
572  if (targetName == clientName) {
573  tclx = clx;
574  } else {
575  tclx = cliTbl->getClient(targetName);
576  if (tclx == 0) {
577  cliTbl->releaseClient(clx);
578  reply = "no such target client";
579  return;
580  }
581  ClientTable::privileges priv = cliTbl->getPrivileges(clx);
582  if (priv != ClientTable::ADMIN && priv != ClientTable::ROOT) {
583  reply = "this operation requires administrative "
584  "privileges";
585  cliTbl->releaseClient(clx);
586  cliTbl->releaseClient(tclx);
587  return;
588  }
589  ClientTable::privileges tpriv = cliTbl->getPrivileges(tclx);
590  if (priv != ClientTable::ROOT &&
591  (tpriv == ClientTable::ROOT ||
592  tpriv == ClientTable::ADMIN)) {
593  reply = "this operation requires root privileges";
594  cliTbl->releaseClient(clx);
595  cliTbl->releaseClient(tclx);
596  return;
597  }
598  }
599  string item; RateSpec rates;
600  ClientTable::privileges priv = cliTbl->getPrivileges(clx);
601  // release locks while getting profile data
602  cliTbl->releaseClient(clx);
603  if (tclx != clx) cliTbl->releaseClient(tclx);
604  string realName, email; RateSpec defRates, totRates;
605  while (buf.readAlphas(item)) {
606  if (item == "realName" && buf.verify(':') &&
607  buf.readString(realName) && buf.nextLine()) {
608  // that's all for now
609  } else if (item == "email" && buf.verify(':') &&
610  buf.readWord(email) && buf.nextLine()) {
611  // that's all for now
612  } else if (item == "defRates" && buf.verify(':') &&
613  buf.readRspec(defRates) && buf.nextLine()) {
614  // that's all for now
615  } else if (item == "totalRates" && buf.verify(':') &&
616  buf.readRspec(totRates) && buf.nextLine()) {
617  // that's all for now
618  } else if ((item == "over" && buf.nextLine()) ||
619  (item == "overAndOut" && buf.nextLine())) {
620  break;
621  } else {
622  reply = "misformatted request (" + item + ")";
623  return;
624  }
625  }
626  tclx = cliTbl->getClient(targetName);
627  if (tclx == 0) {
628  reply = "could not update target client data";
629  return;
630  }
631  if (realName.length() > 0) cliTbl->setRealName(tclx,realName);
632  if (email.length() > 0) cliTbl->setEmail(tclx,email);
633  if (priv != ClientTable::LIMITED) {
634  if (defRates.isSet() &&
635  defRates.leq(cliTbl->getTotalRates(tclx)))
636  cliTbl->getDefRates(tclx) = defRates;
637  if (totRates.isSet() &&
638  defRates.leq(cliTbl->getTotalRates(tclx)))
639  cliTbl->getTotalRates(tclx) = totRates;
640  }
641  writeRecord(tclx);
642  reply = "profile updated";
643  cliTbl->releaseClient(tclx);
644  return;
645 }
646 
647 void changePassword(NetBuffer& buf, string& clientName, string& reply) {
648  string s, targetName;
649  if (!buf.verify(':') || !buf.readAlphas(targetName) || !buf.nextLine()){
650  reply = "could not read target name"; return;
651  }
652  int clx = cliTbl->getClient(clientName);
653  if (clx == 0) {
654  reply = "cannot access client data(" + clientName + ")";
655  return;
656  }
657  int tclx;
658  if (targetName == clientName) {
659  tclx = clx;
660  cliTbl->releaseClient(clx);
661  } else {
662  tclx = cliTbl->getClient(targetName);
663  if (tclx == 0) {
664  reply = "no such target"; return;
665  }
666  ClientTable::privileges priv = cliTbl->getPrivileges(clx);
667  ClientTable::privileges tpriv = cliTbl->getPrivileges(tclx);
668  cliTbl->releaseClient(clx);
669  cliTbl->releaseClient(tclx);
670  if (priv != ClientTable::ADMIN && priv != ClientTable::ROOT) {
671  reply = "this operation requires administrative "
672  "privileges";
673  return;
674  }
675  if (priv != ClientTable::ROOT &&
676  (tpriv == ClientTable::ROOT ||
677  tpriv == ClientTable::ADMIN)) {
678  reply = "this operation requires root privileges";
679  return;
680  }
681  }
682  string item, pwd;
683  while (buf.readAlphas(item)) {
684 cerr << "item=" << item << "\nbuf=" << buf.toString(s);
685  if (item == "password" && buf.verify(':') &&
686  buf.readWord(pwd) && buf.nextLine()) {
687  // nothing more for now
688  } else if ((item == "over" && buf.nextLine()) ||
689  (item == "overAndOut" && buf.nextLine())) {
690  break;
691  } else {
692  reply = "misformatted request (" + item + ")";
693  return;
694  }
695  }
696  tclx = cliTbl->getClient(targetName);
697  if (tclx == 0) {
698  reply = "cannot access target client data(" + targetName + ")";
699  return;
700  }
701  cliTbl->setPassword(tclx,pwd);
702  writeRecord(tclx);
703  cliTbl->releaseClient(tclx);
704  return;
705 }
706 
707 void uploadPhoto(int sock, NetBuffer& buf, string& clientName, string& reply) {
708  int length;
709  if (!buf.verify(':') || !buf.readInt(length) || !buf.nextLine()) {
710  reply = "cannot read length"; return;
711  }
712  if (length > 50000) {
713  reply = "photo file exceeds 50000 byte limit"; return;
714  }
715  int clx = cliTbl->getClient(clientName);
716  ofstream photoFile;
717  photoFile.open("clientPhotos/" + clientName + ".jpg",ofstream::binary);
718  if (!photoFile.good()) {
719  reply = "cannot open photo file"; return;
720  }
721  cliTbl->releaseClient(clx);
722  Np4d::sendString(sock,"proceed\n");
723  char xbuf[1001];
724  int numRcvd = 0;
725  while (true) {
726  int n = buf.readBlock(xbuf,min(1000,length-numRcvd));
727  if (n <= 0) break;
728  photoFile.write(xbuf,n);
729  numRcvd += n;
730  if (numRcvd == length) break;
731  }
732  if (numRcvd != length) {
733  reply = "could not transfer complete file"; return;
734  }
735  string s1, s2;
736  if (!buf.readLine(s1) || s1 != "photo finished" ||
737  !buf.readLine(s2) || s2 != "over") {
738  reply = "file transfer incomplete"; return;
739  }
740  photoFile.close();
741  reply = "photo received";
742  return;
743 }
744 
745 void getSessions(NetBuffer& buf, string& clientName, string& reply) {
746  // form a string with one line for each session
747  // where each line starts with word "session",
748  // followed by the fields defined for each session
749  // return this as the value of reply
750 }
751 
752 void cancelSession(NetBuffer& buf, string& clientName, string& reply) {
753  // check buf for ':' followed by a forest address
754  // use the forest address to identify the session
755  // send a cancel session control packet to net manager
756  // and if that completes successfully, remove session from
757  // ClientTable data structure, write account record and return
758  // on failure, set reply to indicate the cause of the failure
759 }
760 
761 void addComtree(NetBuffer& buf, string& clientName, string& reply) {
762 }
763 
764 bool handleConnDisc(pktx px, CtlPkt& cp, CpHandler& cph) {
765  Packet& p = ps->getPacket(px);
766  if (p.srcAdr != nmAdr || cp.mode != CtlPkt::REQUEST) {
767  return false;
768  }
769  fAdr_t cliAdr = cp.adr1;
770  int sess = cliTbl->getSession(cliAdr); // locks client
771  if (sess == 0) {
772  cph.errReply(px,cp,"no record of session for "
773  "specified client address");
774  return false;
775  }
776  int clx = cliTbl->getClientIndex(sess);
777  const string& cliName = cliTbl->getClientName(clx);
778  ipa_t cliIp = cliTbl->getClientIp(sess);
779  fAdr_t rtrAdr = cliTbl->getRouterAdr(sess);
780 
781  if (cp.type == CtlPkt::CLIENT_DISCONNECT) {
782  cliTbl->removeSession(sess);
783  }
784 
785  cliTbl->releaseClient(clx);
786  acctRecType typ = (cp.type == CtlPkt::CLIENT_CONNECT ?
787  CONNECT_REC : DISCONNECT_REC);
788  writeAcctRecord(cliName, cliAdr, cliIp, rtrAdr, typ);
789 
790  // send reply to original request
791  CtlPkt repCp(cp.type,CtlPkt::POS_REPLY,cp.seqNum);
792  cph.sendReply(repCp, p.srcAdr);
793  return true;
794 }
795 
803 void writeAcctRecord(const string& cname, fAdr_t cliAdr, ipa_t cliIp,
804  fAdr_t rtrAdr, acctRecType recType) {
805  // should really lock while writing
806  if (!acctFile.good()) {
807  logger->log("ClientMgr::writeAcctRecord: cannot write "
808  "to accouting file",2);
809  return;
810  }
811  string typeStr = (recType == NEWSESSION ? "new session" :
812  (recType == CONNECT_REC ? "connect" :
813  (recType == DISCONNECT_REC ?
814  "disconnect" : "undefined record")));
815  time_t t = Misc::currentTime();
816  string now = string(ctime(&t)); now.erase(now.end()-1);
817  string s;
818  acctFile << typeStr << ", " << now << ", " << cname << ", "
819  << Np4d::ip2string(cliIp,s) << ", ";
820  acctFile << Forest::fAdr2string(cliAdr,s) << ", ";
821  acctFile << Forest::fAdr2string(rtrAdr,s) << "\n";
822  acctFile.flush();
823 }
824 
825 /*
826 void writeClientLog(int clx) {
827  // should really lock while writing
828  if (!clientLog.good()) {
829  logger->log("ClientMgr::writeClientLog: cannot write "
830  "to client log file",2);
831  return;
832  }
833  string s;
834  clientLog << cliTbl->client2string(clx,s);
835  clientLog.flush();
836 }
837 */
838 
843 void writeRecord(int clx) {
844  if (clx < 0 || clx >= cliTbl->getMaxClients()) return;
845 
846  pthread_mutex_lock(&clientFileLock);
847  if (dummyRecord == 0) {
848  // create dummy record, for padding clientFile
849  dummyRecord = new char[RECORD_SIZE];
850  for (char *p = dummyRecord; p < dummyRecord+RECORD_SIZE; p++)
851  *p = ' ';
852  dummyRecord[0] = '-'; dummyRecord[RECORD_SIZE-1] = '\n';
853  }
854  if (maxRecord == 0) {
855  clientFile.seekp(0,ios_base::end);
856  maxRecord = clientFile.tellp()/RECORD_SIZE;
857  }
858 
859 
860  // position file pointer, adding dummy records if needed
861  if (clx > maxRecord) {
862  clientFile.seekp((maxRecord+1)*RECORD_SIZE);
863  while (clx > maxRecord) {
865  maxRecord++;
866  }
867  }
868  clientFile.seekp(clx*RECORD_SIZE);
869 
870  if (cliTbl->validClient(clx)) {
871  string s;
872  cliTbl->client2string(clx,s);
873  s = "+ " + s;
874  if (s.length() > RECORD_SIZE) {
875  s.erase(RECORD_SIZE-1); s += "\n";
876  } else {
877  s.erase(s.length()-1);
878  int len = RECORD_SIZE - s.length();
879  char *p = dummyRecord + s.length();
880  s.append(p,len);
881  }
882  clientFile.write(s.c_str(),RECORD_SIZE);
883  } else {
884  //s.assign(dummyRecord,RECORD_SIZE);
885  clientFile.write(dummyRecord,RECORD_SIZE);
886  }
887  clientFile.flush();
888  maxRecord = max(clx,maxRecord);
889  pthread_mutex_unlock(&clientFileLock);
890  return;
891 }
892 
893 } // ends namespace