VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/service.cpp@ 50724

Last change on this file since 50724 was 50724, checked in by vboxsync, 11 years ago

DnD: Implemented guest->host drag'n drop for X11-based guests.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.1 KB
Line 
1/* $Id: service.cpp 50724 2014-03-07 10:54:59Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_svc_guest_control Guest Control HGCM Service
19 *
20 * This service acts as a proxy for handling and buffering host command requests
21 * and clients on the guest. It tries to be as transparent as possible to let
22 * the guest (client) and host side do their protocol handling as desired.
23 *
24 * The following terms are used:
25 * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
26 * which wants to control something on the guest.
27 * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
28 * new host commands to perform. There can be multiple clients connected
29 * to a service. A client is represented by its HGCM client ID.
30 * - Context ID: An (almost) unique ID automatically generated on the host (Main API)
31 * to not only distinguish clients but individual requests. Because
32 * the host does not know anything about connected clients it needs
33 * an indicator which it can refer to later. This context ID gets
34 * internally bound by the service to a client which actually processes
35 * the command in order to have a relationship between client<->context ID(s).
36 *
37 * The host can trigger commands which get buffered by the service (with full HGCM
38 * parameter info). As soon as a client connects (or is ready to do some new work)
39 * it gets a buffered host command to process it. This command then will be immediately
40 * removed from the command list. If there are ready clients but no new commands to be
41 * processed, these clients will be set into a deferred state (that is being blocked
42 * to return until a new command is available).
43 *
44 * If a client needs to inform the host that something happened, it can send a
45 * message to a low level HGCM callback registered in Main. This callback contains
46 * the actual data as well as the context ID to let the host do the next necessary
47 * steps for this context. This context ID makes it possible to wait for an event
48 * inside the host's Main API function (like starting a process on the guest and
49 * wait for getting its PID returned by the client) as well as cancelling blocking
50 * host calls in order the client terminated/crashed (HGCM detects disconnected
51 * clients and reports it to this service's callback).
52 */
53
54/******************************************************************************
55 * Header Files *
56 ******************************************************************************/
57#ifdef LOG_GROUP
58 #undef LOG_GROUP
59#endif
60#define LOG_GROUP LOG_GROUP_GUEST_DND
61
62#include "dndmanager.h"
63
64/******************************************************************************
65 * Service class declaration *
66 ******************************************************************************/
67
68/**
69 * Specialized drag & drop service class.
70 */
71class DragAndDropService: public HGCM::AbstractService<DragAndDropService>
72{
73public:
74
75 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
76 : HGCM::AbstractService<DragAndDropService>(pHelpers)
77 , m_pManager(0)
78 , m_cClients(0)
79 {}
80
81protected:
82 /* HGCM service implementation */
83 int init(VBOXHGCMSVCFNTABLE *pTable);
84 int uninit();
85 int clientConnect(uint32_t u32ClientID, void *pvClient);
86 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
87 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
88 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
89
90 static DECLCALLBACK(int) progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser);
91 int modeSet(uint32_t u32Mode);
92 inline uint32_t modeGet() { return m_u32Mode; };
93
94 DnDManager *m_pManager;
95
96 uint32_t m_cClients;
97 RTCList<HGCM::Client*> m_clientQueue;
98 uint32_t m_u32Mode;
99};
100
101/******************************************************************************
102 * Service class implementation *
103 ******************************************************************************/
104
105int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
106{
107 /* Register functions. */
108 pTable->pfnHostCall = svcHostCall;
109 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
110 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
111 pTable->pfnRegisterExtension = svcRegisterExtension;
112
113 /* Drag'n drop mode is disabled by default. */
114 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
115
116 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
117
118 return VINF_SUCCESS;
119}
120
121int DragAndDropService::uninit(void)
122{
123 delete m_pManager;
124
125 return VINF_SUCCESS;
126}
127
128int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
129{
130 LogFlowFunc(("New client (%RU32) connected\n", u32ClientID));
131 if (m_cClients < UINT32_MAX)
132 m_cClients++;
133 else
134 AssertMsgFailed(("Maximum number of clients reached\n"));
135
136 /*
137 * Clear the message queue as soon as a new clients connect
138 * to ensure that every client has the same state.
139 */
140 if (m_pManager)
141 m_pManager->clear();
142
143 return VINF_SUCCESS;
144}
145
146int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
147{
148 /* Remove all waiters with this u32ClientID. */
149 for (size_t i = 0; i < m_clientQueue.size(); )
150 {
151 HGCM::Client *pClient = m_clientQueue.at(i);
152 if (pClient->clientId() == u32ClientID)
153 {
154 if (m_pHelpers)
155 m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED);
156
157 m_clientQueue.removeAt(i);
158 delete pClient;
159 }
160 else
161 i++;
162 }
163
164 return VINF_SUCCESS;
165}
166
167int DragAndDropService::modeSet(uint32_t u32Mode)
168{
169 /** @todo Validate mode. */
170 switch (u32Mode)
171 {
172 case VBOX_DRAG_AND_DROP_MODE_OFF:
173 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
174 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
175 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
176 m_u32Mode = u32Mode;
177 break;
178
179 default:
180 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
181 break;
182 }
183
184 return VINF_SUCCESS;
185}
186
187void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
188 void *pvClient, uint32_t u32Function,
189 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
190{
191 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
192 u32ClientID, u32Function, cParms));
193
194 /* Check if we've the right mode set. */
195 int rc = VERR_ACCESS_DENIED; /* Play safe. */
196 switch (u32Function)
197 {
198 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
199 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
200 {
201 rc = VINF_SUCCESS;
202 }
203 else
204 {
205 LogFlowFunc(("DnD disabled, deferring request\n"));
206 rc = VINF_HGCM_ASYNC_EXECUTE;
207 }
208 break;
209 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
210 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
211 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
212 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
213 {
214 rc = VINF_SUCCESS;
215 }
216 else
217 LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
218 break;
219 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
220 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
221 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
222 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
223 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
224#ifdef VBOX_WITH_DRAG_AND_DROP_GH
225 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
226 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
227 {
228 rc = VINF_SUCCESS;
229 }
230 else
231#endif
232 LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
233 break;
234 default:
235 /* Reach through to DnD manager. */
236 rc = VINF_SUCCESS;
237 break;
238 }
239
240#ifdef DEBUG_andy
241 LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
242#endif
243
244 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
245 {
246 switch (u32Function)
247 {
248 /* Note: Older VBox versions with enabled DnD guest->host support (< 4.4)
249 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
250 * HOST_DND_GH_REQ_PENDING, which led this service returning
251 * VERR_INVALID_PARAMETER when the guest wanted to actually
252 * handle HOST_DND_GH_REQ_PENDING. */
253 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
254 {
255 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
256 if ( cParms != 3
257 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
258 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
259 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
260 rc = VERR_INVALID_PARAMETER;
261 else
262 {
263 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32, &paParms[1].u.uint32);
264 if ( RT_FAILURE(rc)
265 && paParms[2].u.uint32) /* Blocking? */
266 {
267 /* Defer client returning. */
268 rc = VINF_HGCM_ASYNC_EXECUTE;
269 }
270 }
271 break;
272 }
273 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
274 {
275 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
276 if ( cParms != 1
277 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
278 rc = VERR_INVALID_PARAMETER;
279 else
280 {
281 DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
282 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
283 paParms[0].getUInt32(&data.uAction); /* Get drop action. */
284 if (m_pfnHostCallback)
285 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
286 }
287 break;
288 }
289 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
290 {
291 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
292 if ( cParms != 1
293 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
294 rc = VERR_INVALID_PARAMETER;
295 else
296 {
297 DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
298 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
299 uint32_t cTmp;
300 paParms[0].getPointer((void**)&data.pszFormat, &cTmp);
301 if (m_pfnHostCallback)
302 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
303 }
304 break;
305 }
306#ifdef VBOX_WITH_DRAG_AND_DROP_GH
307 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
308 {
309 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
310 if ( cParms != 3
311 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
312 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* allactions */
313 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
314 rc = VERR_INVALID_PARAMETER;
315 else
316 {
317 DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
318 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
319 paParms[0].getUInt32(&data.uDefAction);
320 paParms[1].getUInt32(&data.uAllActions);
321 uint32_t cTmp;
322 paParms[2].getPointer((void**)&data.pszFormat, &cTmp);
323 if (m_pfnHostCallback)
324 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
325 }
326 break;
327 }
328 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
329 {
330 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
331 if ( cParms != 2
332 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* data */
333 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
334 rc = VERR_INVALID_PARAMETER;
335 else
336 {
337 DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
338 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
339 paParms[0].getPointer((void**)&data.pvData, &data.cbData);
340 paParms[1].getUInt32(&data.cbTotalSize);
341 if (m_pfnHostCallback)
342 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
343 }
344 break;
345 }
346 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
347 {
348 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
349 if ( cParms != 3
350 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
351 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* path length */
352 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
353 rc = VERR_INVALID_PARAMETER;
354 else
355 {
356 DragAndDropSvc::VBOXDNDCBSNDDIRDATA data;
357 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR;
358 uint32_t cTmp;
359 paParms[0].getPointer((void**)&data.pszPath, &cTmp);
360 paParms[1].getUInt32(&data.cbPath);
361 paParms[2].getUInt32(&data.fMode);
362#ifdef DEBUG_andy
363 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n",
364 data.pszPath, data.cbPath, data.fMode));
365#endif
366 if (m_pfnHostCallback)
367 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
368 }
369 break;
370 }
371 case DragAndDropSvc::GUEST_DND_GH_SND_FILE:
372 {
373 LogFlowFunc(("GUEST_DND_GH_SND_FILE\n"));
374 if ( cParms != 5
375 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* file path */
376 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* file path length */
377 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* file data */
378 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* file data length */
379 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
380 rc = VERR_INVALID_PARAMETER;
381 else
382 {
383 DragAndDropSvc::VBOXDNDCBSNDFILEDATA data;
384 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE;
385 uint32_t cTmp;
386 paParms[0].getPointer((void**)&data.pszFilePath, &cTmp);
387 paParms[1].getUInt32(&data.cbFilePath);
388 paParms[2].getPointer((void**)&data.pvData, &data.cbData);
389 /* paParms[3] is cbData. */
390 paParms[4].getUInt32(&data.fMode);
391#ifdef DEBUG_andy
392 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
393 data.pszFilePath, data.cbData, data.pvData, data.fMode));
394#endif
395 if (m_pfnHostCallback)
396 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
397 }
398 break;
399 }
400 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
401 {
402 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
403 if ( cParms != 1
404 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
405 rc = VERR_INVALID_PARAMETER;
406 else
407 {
408 DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
409 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
410
411 uint32_t rcOp;
412 paParms[0].getUInt32(&rcOp);
413 data.rc = rcOp;
414
415 if (m_pfnHostCallback)
416 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
417 }
418 break;
419 }
420#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
421 default:
422 {
423 /* All other messages are handled by the DnD manager. */
424 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
425 break;
426 }
427 }
428 }
429
430 /* If async execution is requested, we didn't notify the guest yet about
431 * completion. The client is queued into the waiters list and will be
432 * notified as soon as a new event is available. */
433 if (rc == VINF_HGCM_ASYNC_EXECUTE)
434 {
435 m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle,
436 u32Function, cParms, paParms));
437 }
438
439 if ( rc != VINF_HGCM_ASYNC_EXECUTE
440 && m_pHelpers)
441 {
442 m_pHelpers->pfnCallComplete(callHandle, rc);
443 }
444
445 LogFlowFunc(("Returning rc=%Rrc\n", rc));
446}
447
448int DragAndDropService::hostCall(uint32_t u32Function,
449 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
450{
451 LogFlowFunc(("u32Function=%RU32, cParms=%RU32\n", u32Function, cParms));
452
453 int rc;
454 if (u32Function == DragAndDropSvc::HOST_DND_SET_MODE)
455 {
456 if (cParms != 1)
457 rc = VERR_INVALID_PARAMETER;
458 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
459 rc = VERR_INVALID_PARAMETER;
460 else
461 rc = modeSet(paParms[0].u.uint32);
462 }
463 else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
464 {
465 if (!m_clientQueue.isEmpty()) /* At least one client on the guest connected? */
466 {
467 rc = m_pManager->addMessage(u32Function, cParms, paParms);
468 if (RT_SUCCESS(rc))
469 {
470 HGCM::Client *pClient = m_clientQueue.first();
471 AssertPtr(pClient);
472
473 /* Check if this was a request for getting the next host
474 * message. If so, return the message id and the parameter
475 * count. The message itself has to be queued. */
476 uint32_t uMsg = pClient->message();
477 if (uMsg == DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG)
478 {
479 LogFlowFunc(("Client %RU32 is waiting for next host msg\n", pClient->clientId()));
480
481 uint32_t uMsg1;
482 uint32_t cParms1;
483 rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
484 if (RT_SUCCESS(rc))
485 {
486 pClient->addMessageInfo(uMsg1, cParms1);
487 if (m_pHelpers)
488 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
489
490 m_clientQueue.removeFirst();
491 delete pClient;
492 }
493 else
494 AssertMsgFailed(("m_pManager::nextMessageInfo failed with rc=%Rrc\n", rc));
495 }
496 else
497 AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32\n",
498 pClient->clientId(), uMsg));
499 }
500 else
501 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n",
502 u32Function, rc));
503 }
504 else
505 {
506 /* Tell the host that the guest does not support drag'n drop.
507 * This might happen due to not installed Guest Additions or
508 * not running VBoxTray/VBoxClient. */
509 rc = VERR_NOT_SUPPORTED;
510 }
511 }
512 else
513 {
514 /* Tell the host that a wrong drag'n drop mode is set. */
515 rc = VERR_ACCESS_DENIED;
516 }
517
518 LogFlowFunc(("rc=%Rrc\n", rc));
519 return rc;
520}
521
522DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uPercentage, uint32_t uState, int rc, void *pvUser)
523{
524 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
525
526 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
527 AssertPtr(pSelf);
528
529 if (pSelf->m_pfnHostCallback)
530 {
531 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uPercentage=%RU32, uState=%RU32, rc=%Rrc\n",
532 uPercentage, uState, rc));
533 DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA data;
534 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS;
535 data.uPercentage = RT_MIN(uPercentage, 100);
536 data.uState = uState;
537 data.rc = rc;
538
539 return pSelf->m_pfnHostCallback(pSelf->m_pvHostData,
540 DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS,
541 &data, sizeof(data));
542 }
543
544 return VINF_SUCCESS;
545}
546
547/**
548 * @copydoc VBOXHGCMSVCLOAD
549 */
550extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
551{
552 return DragAndDropService::svcLoad(pTable);
553}
554
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette