]> git.lyx.org Git - features.git/blob - src/Server.cpp
9ed5b4ecaffea1d8f2150c4b3d3efb743e10c078
[features.git] / src / Server.cpp
1 /**
2  * \file Server.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author Enrico Forestieri
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 /**
16   Docu   : To use the lyxserver define the name of the pipe in your
17            lyxrc:
18            \serverpipe "/home/myhome/.lyxpipe"
19            Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
20            Each message consists of a single line in ASCII. Input lines
21            (client -> LyX) have the following format:
22             "LYXCMD:<clientname>:<functionname>:<argument>"
23            Answers from LyX look like this:
24            "INFO:<clientname>:<functionname>:<data>"
25  [asierra970531] Or like this in case of error:
26            "ERROR:<clientname>:<functionname>:<error message>"
27            where <clientname> and <functionname> are just echoed.
28            If LyX notifies about a user defined extension key-sequence,
29            the line looks like this:
30            "NOTIFY:<key-sequence>"
31  [asierra970531] New server-only messages to implement a simple protocol
32            "LYXSRV:<clientname>:<protocol message>"
33            where <protocol message> can be "hello" or "bye". If hello is
34            received LyX will inform the client that it's listening its
35            messages, and 'bye' will inform that lyx is closing.
36
37            See development/server_monitor.c for an example client.
38   Purpose: implement a client/server lib for LyX
39 */
40
41 #include <config.h>
42
43 #include "Server.h"
44 #include "FuncRequest.h"
45 #include "LyXAction.h"
46 #include "LyXFunc.h"
47
48 #include "frontends/Application.h"
49
50 #include "support/debug.h"
51 #include "support/FileName.h"
52 #include "support/lstrings.h"
53 #include "support/os.h"
54
55 #include <boost/bind.hpp>
56
57 #ifdef _WIN32
58 #include <QCoreApplication>
59 #endif
60
61 #include <cerrno>
62 #ifdef HAVE_SYS_STAT_H
63 # include <sys/stat.h>
64 #endif
65 #include <fcntl.h>
66
67 using namespace std;
68 using namespace lyx::support;
69 using os::external_path;
70
71 namespace lyx {
72
73 /////////////////////////////////////////////////////////////////////
74 //
75 // LyXComm
76 //
77 /////////////////////////////////////////////////////////////////////
78
79 #if defined(_WIN32)
80
81 class PipeEvent : public QEvent {
82 public:
83         ///
84         PipeEvent(HANDLE inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
85         {}
86         ///
87         HANDLE pipe() const { return inpipe_; }
88
89 private:
90         HANDLE inpipe_;
91 };
92
93 namespace {
94
95 bool closing_ = false;
96
97 void closing()
98 {
99         closing_ = true;
100 }
101
102
103 char * errormsg()
104 {
105         void * msgbuf;
106         DWORD error = GetLastError();
107         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
108                       FORMAT_MESSAGE_FROM_SYSTEM |
109                       FORMAT_MESSAGE_IGNORE_INSERTS,
110                       NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
111                       (LPTSTR) &msgbuf, 0, NULL);
112         return static_cast<char *>(msgbuf);
113 }
114
115
116 extern "C" {
117
118 DWORD WINAPI pipeServerWrapper(void * arg)
119 {
120         LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
121         lyxcomm->pipeServer();
122         return 1;
123 }
124
125 }
126
127 } // namespace anon
128
129 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
130         : pipename_(pip), client_(cli), clientcb_(ccb)
131 {
132         // Ask Qt to notify us on quit.
133         qAddPostRoutine(closing);
134         ready_ = false;
135         openConnection();
136 }
137
138
139 void LyXComm::pipeServer()
140 {
141         int const bufsize = 1024;
142         HANDLE inpipe;
143
144         outpipe_ = CreateNamedPipe(external_path(outPipeName()).c_str(),
145                                    PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT,
146                                    PIPE_UNLIMITED_INSTANCES,
147                                    bufsize, 0, 0, NULL);
148         if (outpipe_ == INVALID_HANDLE_VALUE) {
149                 lyxerr << "LyXComm: Could not create pipe "
150                        << outPipeName() << '\n' << errormsg() << endl;
151                 return;
152         }
153         ConnectNamedPipe(outpipe_, NULL);
154         // Now change to blocking mode
155         DWORD mode = PIPE_WAIT;
156         SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
157
158         while (!checkStopServerEvent()) {
159                 inpipe = CreateNamedPipe(external_path(inPipeName()).c_str(),
160                                          PIPE_ACCESS_INBOUND, PIPE_WAIT,
161                                          PIPE_UNLIMITED_INSTANCES,
162                                          0, bufsize, 0, NULL);
163                 if (inpipe == INVALID_HANDLE_VALUE) {
164                         lyxerr << "LyXComm: Could not create pipe "
165                                << inPipeName() << '\n' << errormsg() << endl;
166                         break;
167                 }
168
169                 BOOL connected = ConnectNamedPipe(inpipe, NULL);
170                 // Check whether we are signaled to shutdown the pipe server.
171                 if (checkStopServerEvent()) {
172                         CloseHandle(inpipe);
173                         break;
174                 }
175                 if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
176                         PipeEvent * event = new PipeEvent(inpipe);
177                         QCoreApplication::postEvent(this,
178                                         static_cast<QEvent *>(event));
179                 } else
180                         CloseHandle(inpipe);
181         }
182         CloseHandle(outpipe_);
183 }
184
185
186 bool LyXComm::event(QEvent * e)
187 {
188         if (e->type() == QEvent::User) {
189                 read_ready(static_cast<PipeEvent *>(e)->pipe());
190                 return true;
191         }
192         return false;
193 }
194
195
196 BOOL LyXComm::checkStopServerEvent()
197 {
198         return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0 || closing_;
199 }
200
201
202 void LyXComm::openConnection()
203 {
204         LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
205
206         // If we are up, that's an error
207         if (ready_) {
208                 lyxerr << "LyXComm: Already connected" << endl;
209                 return;
210         }
211         // We assume that we don't make it
212         ready_ = false;
213
214         if (pipename_.empty()) {
215                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
216                 return;
217         }
218
219         stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
220         DWORD tid;
221         HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
222                                      static_cast<void *>(this), 0, &tid);
223         if (!thread) {
224                 lyxerr << "LyXComm: Could not create pipe server thread: "
225                        << errormsg() << endl;
226                 return;
227         } else
228                 CloseHandle(thread);
229
230         // We made it!
231         ready_ = true;
232         LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
233 }
234
235
236 /// Close pipes
237 void LyXComm::closeConnection()
238 {
239         LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
240
241         if (pipename_.empty()) {
242                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
243                 return;
244         }
245
246         if (!ready_) {
247                 LYXERR0("LyXComm: Already disconnected");
248                 return;
249         }
250
251         SetEvent(stopserver_);
252         HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
253                                   GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
254                                   FILE_ATTRIBUTE_NORMAL, NULL);
255         if (hpipe != INVALID_HANDLE_VALUE)
256                 CloseHandle(hpipe);
257
258         ResetEvent(stopserver_);
259         CloseHandle(stopserver_);
260         ready_ = false;
261 }
262
263
264 void LyXComm::emergencyCleanup()
265 {
266         if (!pipename_.empty()) {
267                 SetEvent(stopserver_);
268                 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
269                                           GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
270                                           FILE_ATTRIBUTE_NORMAL, NULL);
271                 if (hpipe != INVALID_HANDLE_VALUE)
272                         CloseHandle(hpipe);
273
274                 ResetEvent(stopserver_);
275                 CloseHandle(stopserver_);
276         }
277 }
278
279 void LyXComm::read_ready(HANDLE inpipe)
280 {
281         string read_buffer_;
282         read_buffer_.erase();
283
284         int const charbuf_size = 100;
285         char charbuf[charbuf_size];
286
287         DWORD status;
288         while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
289                 if (status > 0) {
290                         charbuf[status] = '\0'; // turn it into a c string
291                         read_buffer_ += rtrim(charbuf, "\r");
292                         // commit any commands read
293                         while (read_buffer_.find('\n') != string::npos) {
294                                 // split() grabs the entire string if
295                                 // the delim /wasn't/ found. ?:-P
296                                 string cmd;
297                                 read_buffer_= split(read_buffer_, cmd, '\n');
298                                 cmd = rtrim(cmd, "\r");
299                                 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
300                                         << ", read_buffer_:" << read_buffer_
301                                         << ", cmd:" << cmd);
302                                 if (!cmd.empty())
303                                         clientcb_(client_, cmd);
304                                         //\n or not \n?
305                         }
306                 }
307         }
308         if (GetLastError() != ERROR_BROKEN_PIPE) {
309                 // An error occurred
310                 LYXERR0("LyXComm: " << errormsg());
311                 if (!read_buffer_.empty()) {
312                         LYXERR0("LyXComm: truncated command: " << read_buffer_);
313                         read_buffer_.erase();
314                 }
315         }
316         // Client closed the pipe, so disconnect and close.
317         DisconnectNamedPipe(inpipe);
318         CloseHandle(inpipe);
319         FlushFileBuffers(outpipe_);
320         DisconnectNamedPipe(outpipe_);
321         // Temporarily change to non-blocking mode otherwise
322         // ConnectNamedPipe() would block waiting for a connection.
323         DWORD mode = PIPE_NOWAIT;
324         SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
325         ConnectNamedPipe(outpipe_, NULL);
326         mode = PIPE_WAIT;
327         SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
328 }
329
330
331 void LyXComm::send(string const & msg)
332 {
333         if (msg.empty()) {
334                 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
335                 return;
336         }
337
338         LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
339
340         if (pipename_.empty()) return;
341
342         if (!ready_) {
343                 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
344                 return;
345         }
346
347         bool success;
348         int count = 0;
349         do {
350                 DWORD sent;
351                 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
352                 if (!success) {
353                         DWORD error = GetLastError();
354                         if (error == ERROR_NO_DATA) {
355                                 DisconnectNamedPipe(outpipe_);
356                                 DWORD mode = PIPE_NOWAIT;
357                                 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
358                                 ConnectNamedPipe(outpipe_, NULL);
359                                 mode = PIPE_WAIT;
360                                 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
361                         } else if (error != ERROR_PIPE_LISTENING)
362                                 break;
363                 }
364                 Sleep(100);
365                 ++count;
366         } while (!success && count < 100 && !checkStopServerEvent());
367
368         if (!success) {
369                 lyxerr << "LyXComm: Error sending message: " << msg
370                        << '\n' << errormsg()
371                        << "\nLyXComm: Resetting connection" << endl;
372                 closeConnection();
373                 if (!checkStopServerEvent())
374                         openConnection();
375         }
376 }
377
378
379 #elif !defined (HAVE_MKFIFO)
380 // We provide a stub class that disables the lyxserver.
381
382 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
383 {}
384
385 void LyXComm::openConnection()
386 {}
387
388
389 void LyXComm::closeConnection()
390 {}
391
392
393 int LyXComm::startPipe(string const & filename, bool write)
394 {
395         return -1;
396 }
397
398
399 void LyXComm::endPipe(int & fd, string const & filename, bool write)
400 {}
401
402
403 void LyXComm::emergencyCleanup()
404 {}
405
406 void LyXComm::read_ready()
407 {}
408
409
410 void LyXComm::send(string const & msg)
411 {}
412
413
414 #else // defined (HAVE_MKFIFO)
415
416
417 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
418         : pipename_(pip), client_(cli), clientcb_(ccb)
419 {
420         ready_ = false;
421         openConnection();
422 }
423
424
425 void LyXComm::openConnection()
426 {
427         LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
428
429         // If we are up, that's an error
430         if (ready_) {
431                 lyxerr << "LyXComm: Already connected" << endl;
432                 return;
433         }
434         // We assume that we don't make it
435         ready_ = false;
436
437         if (pipename_.empty()) {
438                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
439                 return;
440         }
441
442         infd_ = startPipe(inPipeName(), false);
443         if (infd_ == -1)
444                 return;
445
446         outfd_ = startPipe(outPipeName(), true);
447         if (outfd_ == -1) {
448                 endPipe(infd_, inPipeName(), false);
449                 return;
450         }
451
452         if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
453                 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
454                        << '\n' << strerror(errno) << endl;
455                 return;
456         }
457
458         // We made it!
459         ready_ = true;
460         LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
461 }
462
463
464 /// Close pipes
465 void LyXComm::closeConnection()
466 {
467         LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
468
469         if (pipename_.empty()) {
470                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
471                 return;
472         }
473
474         if (!ready_) {
475                 LYXERR0("LyXComm: Already disconnected");
476                 return;
477         }
478
479         endPipe(infd_, inPipeName(), false);
480         endPipe(outfd_, outPipeName(), true);
481
482         ready_ = false;
483 }
484
485
486 int LyXComm::startPipe(string const & file, bool write)
487 {
488         static bool stalepipe = false;
489         FileName const filename(file);
490         if (filename.exists()) {
491                 if (!write) {
492                         // Let's see whether we have a stale pipe.
493                         int fd = ::open(filename.toFilesystemEncoding().c_str(),
494                                         O_WRONLY | O_NONBLOCK);
495                         if (fd >= 0) {
496                                 // Another LyX instance is using it.
497                                 ::close(fd);
498                         } else if (errno == ENXIO) {
499                                 // No process is reading from the other end.
500                                 stalepipe = true;
501                                 LYXERR(Debug::LYXSERVER,
502                                         "LyXComm: trying to remove "
503                                         << filename);
504                                 filename.removeFile();
505                         }
506                 } else if (stalepipe) {
507                         LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
508                                 << filename);
509                         filename.removeFile();
510                         stalepipe = false;
511                 }
512                 if (filename.exists()) {
513                         lyxerr << "LyXComm: Pipe " << filename
514                                << " already exists.\nIf no other LyX program"
515                                   " is active, please delete the pipe by hand"
516                                   " and try again."
517                                << endl;
518                         pipename_.erase();
519                         return -1;
520                 }
521         }
522
523         if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
524                 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
525                        << strerror(errno) << endl;
526                 return -1;
527         }
528         int const fd = ::open(filename.toFilesystemEncoding().c_str(),
529                               write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
530
531         if (fd < 0) {
532                 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
533                        << strerror(errno) << endl;
534                 filename.removeFile();
535                 return -1;
536         }
537
538         if (!write) {
539                 theApp()->registerSocketCallback(fd,
540                         boost::bind(&LyXComm::read_ready, this));
541         }
542
543         return fd;
544 }
545
546
547 void LyXComm::endPipe(int & fd, string const & filename, bool write)
548 {
549         if (fd < 0)
550                 return;
551
552         if (!write)
553                 theApp()->unregisterSocketCallback(fd);
554
555         if (::close(fd) < 0) {
556                 lyxerr << "LyXComm: Could not close pipe " << filename
557                        << '\n' << strerror(errno) << endl;
558         }
559
560         if (FileName(filename).removeFile() < 0) {
561                 lyxerr << "LyXComm: Could not remove pipe " << filename
562                        << '\n' << strerror(errno) << endl;
563         }
564
565         fd = -1;
566 }
567
568
569 void LyXComm::emergencyCleanup()
570 {
571         if (!pipename_.empty()) {
572                 endPipe(infd_, inPipeName(), false);
573                 endPipe(outfd_, outPipeName(), true);
574         }
575 }
576
577
578 // Receives messages and sends then to client
579 void LyXComm::read_ready()
580 {
581         // FIXME: make read_buffer_ a class-member for multiple sessions
582         static string read_buffer_;
583         read_buffer_.erase();
584
585         int const charbuf_size = 100;
586         char charbuf[charbuf_size];
587
588         // As O_NONBLOCK is set, until no data is available for reading,
589         // read() doesn't block but returns -1 and set errno to EAGAIN.
590         // After a client that opened the pipe for writing, closes it
591         // (and no other client is using the pipe), read() would always
592         // return 0 and thus the connection has to be reset.
593
594         errno = 0;
595         int status;
596         // the single = is intended here.
597         while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
598
599                 if (status > 0) {
600                         charbuf[status] = '\0'; // turn it into a c string
601                         read_buffer_ += rtrim(charbuf, "\r");
602                         // commit any commands read
603                         while (read_buffer_.find('\n') != string::npos) {
604                                 // split() grabs the entire string if
605                                 // the delim /wasn't/ found. ?:-P
606                                 string cmd;
607                                 read_buffer_= split(read_buffer_, cmd,'\n');
608                                 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
609                                         << ", read_buffer_:" << read_buffer_
610                                         << ", cmd:" << cmd);
611                                 if (!cmd.empty())
612                                         clientcb_(client_, cmd);
613                                         //\n or not \n?
614                         }
615                 } else {
616                         if (errno == EAGAIN) {
617                                 // Nothing to read, continue
618                                 errno = 0;
619                                 return;
620                         }
621                         // An error occurred, better bailing out
622                         LYXERR0("LyXComm: " << strerror(errno));
623                         if (!read_buffer_.empty()) {
624                                 LYXERR0("LyXComm: truncated command: " << read_buffer_);
625                                 read_buffer_.erase();
626                         }
627                         break; // reset connection
628                 }
629         }
630
631         // The connection gets reset when read() returns 0 (meaning that the
632         // last client closed the pipe) or an error occurred, in which case
633         // read() returns -1 and errno != EAGAIN.
634         closeConnection();
635         openConnection();
636         errno = 0;
637 }
638
639
640 void LyXComm::send(string const & msg)
641 {
642         if (msg.empty()) {
643                 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
644                 return;
645         }
646
647         LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
648
649         if (pipename_.empty()) return;
650
651         if (!ready_) {
652                 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
653         } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
654                 lyxerr << "LyXComm: Error sending message: " << msg
655                        << '\n' << strerror(errno)
656                        << "\nLyXComm: Resetting connection" << endl;
657                 closeConnection();
658                 openConnection();
659         }
660 }
661
662 #endif // defined (HAVE_MKFIFO)
663
664
665 string const LyXComm::inPipeName() const
666 {
667         return pipename_ + ".in";
668 }
669
670
671 string const LyXComm::outPipeName() const
672 {
673         return pipename_ + ".out";
674 }
675
676
677 /////////////////////////////////////////////////////////////////////
678 //
679 // Server
680 //
681 /////////////////////////////////////////////////////////////////////
682
683 void ServerCallback(Server * server, string const & msg)
684 {
685         server->callback(msg);
686 }
687
688 Server::Server(LyXFunc * f, string const & pipes)
689         : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
690 {}
691
692
693 Server::~Server()
694 {
695         // say goodbye to clients so they stop sending messages
696         // send as many bye messages as there are clients,
697         // each with client's name.
698         string message;
699         for (int i = 0; i != numclients_; ++i) {
700                 message = "LYXSRV:" + clients_[i] + ":bye\n";
701                 pipes_.send(message);
702         }
703 }
704
705
706 int compare(char const * a, char const * b, unsigned int len)
707 {
708         using namespace std;
709         return strncmp(a, b, len);
710 }
711
712
713 // Handle data gotten from communication, called by LyXComm
714 void Server::callback(string const & msg)
715 {
716         LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
717
718         char const * p = msg.c_str();
719
720         // --- parse the string --------------------------------------------
721         //
722         //  Format: LYXCMD:<client>:<func>:<argstring>\n
723         //
724         bool server_only = false;
725         while (*p) {
726                 // --- 1. check 'header' ---
727
728                 if (compare(p, "LYXSRV:", 7) == 0) {
729                         server_only = true;
730                 } else if (0 != compare(p, "LYXCMD:", 7)) {
731                         lyxerr << "Server: Unknown request \""
732                                << p << '"' << endl;
733                         return;
734                 }
735                 p += 7;
736
737                 // --- 2. for the moment ignore the client name ---
738                 string client;
739                 while (*p && *p != ':')
740                         client += char(*p++);
741                 if (*p == ':')
742                         ++p;
743                 if (!*p)
744                         return;
745
746                 // --- 3. get function name ---
747                 string cmd;
748                 while (*p && *p != ':')
749                         cmd += char(*p++);
750
751                 // --- 4. parse the argument ---
752                 string arg;
753                 if (!server_only && *p == ':' && *(++p)) {
754                         while (*p && *p != '\n')
755                                 arg += char(*p++);
756                         if (*p) ++p;
757                 }
758
759                 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
760                         << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
761
762                 // --- lookup and exec the command ------------------
763
764                 if (server_only) {
765                         string buf;
766                         // return the greeting to inform the client that
767                         // we are listening.
768                         if (cmd == "hello") {
769                                 // One more client
770                                 if (numclients_ == MAX_CLIENTS) { //paranoid check
771                                         LYXERR(Debug::LYXSERVER, "Server: too many clients...");
772                                         return;
773                                 }
774                                 int i = 0;
775                                 while (!clients_[i].empty() && i < numclients_)
776                                         ++i;
777                                 clients_[i] = client;
778                                 ++numclients_;
779                                 buf = "LYXSRV:" + client + ":hello\n";
780                                 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
781                                 pipes_.send(buf);
782                         } else if (cmd == "bye") {
783                                 // If clients_ == 0 maybe we should reset the pipes
784                                 // to prevent fake callbacks
785                                 int i = 0; //look if client is registered
786                                 for (; i < numclients_; ++i) {
787                                         if (clients_[i] == client)
788                                                 break;
789                                 }
790                                 if (i < numclients_) {
791                                         --numclients_;
792                                         clients_[i].erase();
793                                         LYXERR(Debug::LYXSERVER, "Server: Client "
794                                                 << client << " said goodbye");
795                                 } else {
796                                         LYXERR(Debug::LYXSERVER,
797                                                 "Server: ignoring bye messge from unregistered client" << client);
798                                 }
799                         } else {
800                                 LYXERR0("Server: Undefined server command " << cmd << '.');
801                         }
802                         return;
803                 }
804
805                 if (!cmd.empty()) {
806                         // which lyxfunc should we let it connect to?
807                         // The correct solution would be to have a
808                         // specialized (non-gui) BufferView. But how do
809                         // we do it now? Probably we should just let it
810                         // connect to the lyxfunc in the single LyXView we
811                         // support currently. (Lgb)
812
813                         func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
814                         string const rval = to_utf8(func_->getMessage());
815
816                         // all commands produce an INFO or ERROR message
817                         // in the output pipe, even if they do not return
818                         // anything. See chapter 4 of Customization doc.
819                         string buf;
820                         if (func_->errorStat())
821                                 buf = "ERROR:";
822                         else
823                                 buf = "INFO:";
824                         buf += client + ':' + cmd + ':' +  rval + '\n';
825                         pipes_.send(buf);
826
827                         // !!! we don't do any error checking -
828                         //  if the client won't listen, the
829                         //  message is lost and others too
830                         //  maybe; so the client should empty
831                         //  the outpipe before issuing a request.
832
833                         // not found
834                 }
835         }  // while *p
836 }
837
838
839 // Send a notify message to a client, called by WorkAreaKeyPress
840 void Server::notifyClient(string const & s)
841 {
842         pipes_.send("NOTIFY:" + s + "\n");
843 }
844
845
846 } // namespace lyx
847
848 #ifdef _WIN32
849 #include "moc_Server.cpp"
850 #endif