]> git.vomp.tv Git - vompclient.git/blob - src/vconnect.cc
VDPC: Remove any duplicate server entry from the IPv not preferred
[vompclient.git] / src / vconnect.cc
1 /*
2     Copyright 2004-2020 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "defines.h"
21 #include "config.h"
22 #include "video.h"
23 #include "colour.h"
24 #include "control.h"
25 #include "i18n.h"
26 #include "boxstack.h"
27 #include "message.h"
28 #include "log.h"
29 #include "vdr.h"
30 #include "wol.h"
31 #include "vserverselect.h"
32 #include "messagequeue.h"
33 #include "util.h"
34
35 #include "vconnect.h"
36
37 static const char* TAG = "VConnect";
38
39 VConnect::VConnect()
40 {
41   boxstack = BoxStack::getInstance();
42   vdr = VDR::getInstance();
43   logger = LogNT::getInstance();
44
45   setSize(400, 200);
46   createBuffer();
47   if (Video::getInstance()->getFormat() == Video::PAL)
48   {
49     setPosition(170, 200);
50   }
51   else
52   {
53     setPosition(160, 150);
54   }
55
56   exitable = 0;
57 }
58
59 VConnect::~VConnect()
60 {
61   threadReqQuit = true;
62   vdpc.stop();
63   stop();
64 }
65
66 void VConnect::draw()
67 {
68   VInfo::draw();
69   logger->debug(TAG, "Draw done");
70 }
71
72 int VConnect::handleCommand(int /* command */)
73 {
74   return BoxStack::ABANDON_COMMAND;
75 }
76
77 void VConnect::run()
78 {
79   threadMutex.lock();
80   threadReqQuit = false;
81   connectThread = std::thread([this]
82   {
83     threadMutex.lock();
84     threadMutex.unlock();
85     threadMethod();
86   });
87   threadMutex.unlock();
88 }
89
90 void VConnect::stop()
91 {
92   threadMutex.lock();
93   threadReqQuit = true;
94   vdr->abortConnect(); // If this and vdr are connecting, cancel it
95   threadCond.notify_one();
96   threadMutex.unlock();
97   connectThread.join();
98 }
99
100 void VConnect::processMessage(Message* m)
101 {
102   if (m->message == Message::SERVER_SELECTED)
103   {
104     serverSelectResponse = m->parameter;
105     threadCond.notify_one();
106   }
107 }
108
109 void VConnect::threadMethod()
110 {
111   logger->debug(TAG, "start threadMethod");
112   Config* config = Config::getInstance();
113
114   // Try to get server and port from config. If successful then
115   // either a server &? port was supplied in config.json or
116   // overridden by command line args
117
118   VDRServer serverFromConfig;
119   if (config->getString("server", "address", serverFromConfig.ip))
120   {
121     logger->debug(TAG, "Server read from config: {}", serverFromConfig.ip);
122     serverFromConfig.port = 3024;
123     int newPort; // copy around because of type mismatch
124     if (config->getInt("server", "port", newPort))
125     {
126       serverFromConfig.port = static_cast<USHORT>(newPort);
127       logger->debug(TAG, "Port read fron config: {}", serverFromConfig.port);
128     }
129
130     while(1) // Only attempt connection to this server from config
131     {
132       if (threadReqQuit) return;
133       if (attemptConnect(&serverFromConfig)) return;
134     }
135   }
136
137   // No config server. Start VDPC
138
139   setOneLiner(tr("Locating server"));
140   draw();
141   boxstack->update(this);
142
143   if (threadReqQuit) return;
144
145   if (!vdpc.init())
146   {
147     logger->crit(TAG, "Failed to init VDPC");
148     return;
149   }
150
151   while(1)
152   {
153     if (threadReqQuit) return;
154     int numServers = vdpc.go();
155     if (threadReqQuit) return;
156
157     if (numServers == 0)
158     {
159       logger->crit(TAG, "VDPC returned 0 servers");
160       return;
161     }
162
163     // Now we have > 0 servers found
164
165     for (int i = 0; i < numServers; i++)
166       logger->info(TAG, "Found server: {} {} {} {}", vdpc[i].ipVersion, vdpc[i].ip.c_str(), vdpc[i].name.c_str(), vdpc[i].port, vdpc[i].version);
167
168     if (numServers == 1)
169     {
170       if (threadReqQuit) return;
171       if (attemptConnect(&vdpc[0])) return; // successful connect
172       continue; // restart discovery
173     }
174
175     // Multiple servers found
176
177
178     /*
179
180     // Special case: If there are two servers returned and they only differ by IP version and IP, select one and connect automatically
181
182     if (     (numServers == 2)
183           && !vdpc[0].name.compare(vdpc[1].name)       // if the names are the same...
184           && (vdpc[0].port == vdpc[1].port)            // and the port number is the same...
185           && (vdpc[0].ipVersion != vdpc[1].ipVersion)  // and the IP versions DO NOT match...
186           / * and the IPs don't match - they can't if ipVersions are different * /
187        )
188     {
189       int which = 6;
190       config->getInt("server-discovery", "prefer-ipv", which);
191
192       logger->debug(TAG, "Auto select IPv{}", which);
193
194       const VDRServer* selected;
195       if (vdpc[0].ipVersion == which) selected = &vdpc[0];
196       else selected = &vdpc[1];
197
198       if (attemptConnect(selected)) return; // successful connect
199
200       // Try the other IPv ?!
201       logger->warn(TAG, "Switching IPv");
202
203       if (vdpc[0].ipVersion == which) selected = &vdpc[1];
204       else selected = &vdpc[0];
205
206       if (attemptConnect(selected)) return; // successful connect
207
208       continue; // restart discovery
209     }
210
211     */
212
213
214     // Now numServers > 1
215
216     VServerSelect* vs = new VServerSelect(vdpc, this); // deleted by handleCommand returning BoxStack::DELETE_ME
217     vs->draw();
218     boxstack->add(vs);
219     boxstack->update(vs);
220
221     {
222       std::unique_lock<std::mutex> ul(threadMutex);
223       if (threadReqQuit) return;
224       threadCond.wait(ul);
225       // This thread has been notified by VServerSelect. serverSelectResponse is now an index into vdpc of the chosen server
226     }
227
228     if (threadReqQuit) return;
229
230     if (attemptConnect(&vdpc[serverSelectResponse])) return; // successful connect
231     continue; // restart discovery
232   }
233 }
234
235 bool VConnect::attemptConnect(const VDRServer* vdrServer)
236 {
237   logger->info(TAG, "Attempting connect to {} {}", vdrServer->ip, vdrServer->port);
238
239   vdr->setServerIP(vdrServer->ip.c_str());
240   vdr->setServerPort(vdrServer->port);
241
242   if (threadReqQuit) return false;
243
244   setOneLiner(tr("Connecting to VDR"));
245   draw();
246   boxstack->update(this);
247
248   bool connectSuccess = vdr->connect();
249   if (threadReqQuit)
250   {
251     vdr->disconnect();
252     return false;
253   }
254
255   if (!connectSuccess)
256   {
257     setOneLiner(tr("Connection failed"));
258     draw();
259     boxstack->update(this);
260     {
261       std::unique_lock<std::mutex> ul(threadMutex);
262       if (threadReqQuit) return false;
263       threadCond.wait_for(ul, std::chrono::milliseconds(3000), [this]{ return threadReqQuit; });
264     }
265     return false;
266   }
267
268   logger->debug(TAG, "Connected ok, doing login");
269
270   // FIXME - what is all this? Can it be moved to NCONFIG?
271   unsigned int version_server_min, version_server_max, version_client;
272   int subtitles;
273   bool loginSuccess = vdr->doLogin(&version_server_min, &version_server_max, &version_client, Control::getInstance()->getASLList(), subtitles);
274
275   if (!loginSuccess)
276   {
277     vdr->disconnect();
278     if ((version_server_min > version_client) || (version_client > version_server_max))
279     {
280       char buffer[1024];
281       sprintf(buffer, "Version error: s min: %x s max: %x c: %x", version_server_min, version_server_max, version_client);
282       setOneLiner(buffer);
283     }
284     else
285     {
286       setOneLiner(tr("Login failed"));
287     }
288
289     draw();
290     boxstack->update(this);
291
292     {
293       std::unique_lock<std::mutex> ul(threadMutex);
294       if (threadReqQuit) return false;
295       threadCond.wait_for(ul, std::chrono::milliseconds(3000), [this]{ return threadReqQuit; });
296     }
297
298     return false;
299   }
300
301   logger->debug(TAG, "VDR login ok");
302
303   Config::getInstance()->set("subtitles", "default", subtitles); // FIXME move this directly into vdr.cc ?
304   Wol::getInstance()->setWakeUpIP(vdrServer->ip.c_str());
305
306   logger->info(TAG, "Send VDR connected message");
307   Message* m = new Message(); // Must be done after this thread ends
308   m->from = this;
309   m->p_to = Message::CONTROL;
310   m->message = Message::VDR_CONNECTED;
311   MessageQueue::getInstance()->postMessage(m);
312
313   return true;
314 }