VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxTray/VBoxDnDDataObject.cpp@ 50752

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

DnD: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.0 KB
Line 
1/* $Id: VBoxDnDDataObject.cpp 50460 2014-02-14 09:46:58Z vboxsync $ */
2/** @file
3 * VBoxDnDDataObject.cpp - IDataObject implementation.
4 */
5
6/*
7 * Copyright (C) 2013-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#include <windows.h>
18#include <new> /* For bad_alloc. */
19#include <shlobj.h>
20
21#include <iprt/path.h>
22#include <iprt/semaphore.h>
23#include <iprt/uri.h>
24
25#include "VBoxTray.h"
26#include "VBoxHelpers.h"
27#include "VBoxDnD.h"
28
29#ifdef DEBUG
30 /* Enable the following line to get much more debug output about
31 * (un)known clipboard formats. */
32 //#define VBOX_DND_DEBUG_FORMATS
33#endif
34
35/** @todo Implement IDataObjectAsyncCapability interface? */
36
37VBoxDnDDataObject::VBoxDnDDataObject(FORMATETC *pFormatEtc,
38 STGMEDIUM *pStgMed, ULONG cFormats)
39 : mStatus(Uninitialized),
40 mRefCount(1),
41 mcFormats(0),
42 mpvData(NULL),
43 mcbData(0)
44{
45 HRESULT hr;
46
47 /* Make sure that there's enough room for our fixed formats. */
48 ULONG cAllFormats = cFormats + 1;
49
50 try
51 {
52 mpFormatEtc = new FORMATETC[cAllFormats];
53 RT_BZERO(mpFormatEtc, sizeof(FORMATETC) * cAllFormats);
54 mpStgMedium = new STGMEDIUM[cAllFormats];
55 RT_BZERO(mpStgMedium, sizeof(STGMEDIUM) * cAllFormats);
56
57 if ( pFormatEtc
58 && pStgMed)
59 {
60 for (ULONG i = 0; i < cFormats; i++)
61 {
62 LogFlowFunc(("Format %RU32: cfFormat=%RI16, tyMed=%RU32, dwAspect=%RU32\n",
63 i, pFormatEtc[i].cfFormat, pFormatEtc[i].tymed, pFormatEtc[i].dwAspect));
64 mpFormatEtc[i] = pFormatEtc[i];
65 mpStgMedium[i] = pStgMed[i];
66 }
67 }
68
69 hr = S_OK;
70 }
71 catch (std::bad_alloc &)
72 {
73 hr = E_OUTOFMEMORY;
74 }
75
76 if (SUCCEEDED(hr))
77 {
78 int rc2 = RTSemEventCreate(&mSemEvent);
79 AssertRC(rc2);
80
81 /* Most commonly used format. */
82 RegisterFormat(&mpFormatEtc[cFormats], CF_HDROP);
83 mpStgMedium[cFormats++].tymed = TYMED_HGLOBAL;
84#if 0
85 /* IStream. */
86 RegisterFormat(&mpFormatEtc[cFormats++],
87 RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
88 RegisterFormat(&mpFormatEtc[cFormats++],
89 RegisterClipboardFormat(CFSTR_FILECONTENTS),
90 TYMED_ISTREAM, 0 /* lIndex */);
91
92 /* Required for e.g. Windows Media Player. */
93 RegisterFormat(&mpFormatEtc[cFormats++],
94 RegisterClipboardFormat(CFSTR_FILENAME));
95 RegisterFormat(&mpFormatEtc[cFormats++],
96 RegisterClipboardFormat(CFSTR_FILENAMEW));
97 RegisterFormat(&mpFormatEtc[cFormats++],
98 RegisterClipboardFormat(CFSTR_SHELLIDLIST));
99 RegisterFormat(&mpFormatEtc[cFormats++],
100 RegisterClipboardFormat(CFSTR_SHELLIDLISTOFFSET));
101#endif
102 mcFormats = cFormats;
103 mStatus = Initialized;
104 }
105
106 LogFlowFunc(("cFormats=%RU32, hr=%Rhrc\n", cFormats, hr));
107}
108
109VBoxDnDDataObject::~VBoxDnDDataObject(void)
110{
111 if (mpFormatEtc)
112 delete[] mpFormatEtc;
113
114 if (mpStgMedium)
115 delete[] mpStgMedium;
116
117 if (mpvData)
118 RTMemFree(mpvData);
119
120 LogFlowFunc(("mRefCount=%RI32\n", mRefCount));
121}
122
123/* static */
124int VBoxDnDDataObject::CreateDataObject(FORMATETC *pFormatEtc, STGMEDIUM *pStgMeds,
125 ULONG cFormats, IDataObject **ppDataObject)
126{
127 AssertPtrReturn(pFormatEtc, VERR_INVALID_POINTER);
128 AssertPtrReturn(pStgMeds, VERR_INVALID_POINTER);
129 AssertPtrReturn(ppDataObject, VERR_INVALID_POINTER);
130
131 int rc;
132 try
133 {
134 *ppDataObject = new VBoxDnDDataObject(pFormatEtc, pStgMeds, cFormats);
135 rc = VINF_SUCCESS;
136 }
137 catch(std::bad_alloc &)
138 {
139 rc = VERR_NO_MEMORY;
140 }
141
142 return rc;
143}
144
145/*
146 * IUnknown methods.
147 */
148
149STDMETHODIMP_(ULONG) VBoxDnDDataObject::AddRef(void)
150{
151 return InterlockedIncrement(&mRefCount);
152}
153
154STDMETHODIMP_(ULONG) VBoxDnDDataObject::Release(void)
155{
156 LONG lCount = InterlockedDecrement(&mRefCount);
157 if (lCount == 0)
158 {
159 delete this;
160 return 0;
161 }
162
163 return lCount;
164}
165
166STDMETHODIMP VBoxDnDDataObject::QueryInterface(REFIID iid, void **ppvObject)
167{
168 AssertPtrReturn(ppvObject, E_INVALIDARG);
169
170 if ( iid == IID_IDataObject
171 || iid == IID_IUnknown)
172 {
173 AddRef();
174 *ppvObject = this;
175 return S_OK;
176 }
177
178 *ppvObject = 0;
179 return E_NOINTERFACE;
180}
181
182/**
183 * Retrieves the data stored in this object and store the result in
184 * pMedium.
185 *
186 * @return IPRT status code.
187 * @return HRESULT
188 * @param pFormatEtc
189 * @param pMedium
190 */
191STDMETHODIMP VBoxDnDDataObject::GetData(FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
192{
193 AssertPtrReturn(pFormatEtc, DV_E_FORMATETC);
194 AssertPtrReturn(pMedium, DV_E_FORMATETC);
195
196 ULONG lIndex;
197 if (!LookupFormatEtc(pFormatEtc, &lIndex)) /* Format supported? */
198 return DV_E_FORMATETC;
199 if (lIndex >= mcFormats) /* Paranoia. */
200 return DV_E_FORMATETC;
201
202 LogFlowFunc(("pFormatEtc=%p, pMedium=%p\n", pFormatEtc, pMedium));
203
204 FORMATETC *pThisFormat = &mpFormatEtc[lIndex];
205 AssertPtr(pThisFormat);
206
207 STGMEDIUM *pThisMedium = &mpStgMedium[lIndex];
208 AssertPtr(pThisMedium);
209
210 HRESULT hr = DV_E_FORMATETC;
211
212 LogFlowFunc(("mStatus=%ld\n", mStatus));
213 if (mStatus == Dropping)
214 {
215 LogFlowFunc(("Waiting for event ...\n"));
216 int rc2 = RTSemEventWait(mSemEvent, RT_INDEFINITE_WAIT);
217 LogFlowFunc(("rc=%Rrc, mStatus=%ld\n", rc2, mStatus));
218 }
219
220 if (mStatus == Dropped)
221 {
222 LogFlowFunc(("cfFormat=%RI16, sFormat=%s, tyMed=%RU32, dwAspect=%RU32\n",
223 pThisFormat->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
224 pThisFormat->tymed, pThisFormat->dwAspect));
225 LogFlowFunc(("Got strFormat=%s, pvData=%p, cbData=%RU32\n",
226 mstrFormat.c_str(), mpvData, mcbData));
227
228 if (mstrFormat.equalsIgnoreCase("text/uri-list"))
229 {
230 RTCList<RTCString> lstFilesURI = RTCString((char*)mpvData, mcbData).split("\r\n");
231 RTCList<RTCString> lstFiles;
232 for (size_t i = 0; i < lstFilesURI.size(); i++)
233 {
234 /* Extract path from URI. */
235 char *pszPath = RTUriPath(lstFilesURI.at(i).c_str());
236 if ( pszPath
237 && strlen(pszPath) > 1)
238 {
239 pszPath++; /** @todo Skip first '/' (part of URI). Correct? */
240 pszPath = RTPathChangeToDosSlashes(pszPath, false /* fForce */);
241 lstFiles.append(pszPath);
242 }
243 }
244#ifdef DEBUG
245 LogFlowFunc(("Files (%zu)\n", lstFiles.size()));
246 for (size_t i = 0; i < lstFiles.size(); i++)
247 LogFlowFunc(("\tFile: %s\n", lstFiles.at(i).c_str()));
248#endif
249
250#if 0
251 if ( (pFormatEtc->tymed & TYMED_ISTREAM)
252 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
253 && (pFormatEtc->cfFormat == CF_FILECONTENTS))
254 {
255
256 }
257 else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
258 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
259 && (pFormatEtc->cfFormat == CF_FILEDESCRIPTOR))
260 {
261
262 }
263 else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
264 && (pFormatEtc->cfFormat == CF_PREFERREDDROPEFFECT))
265 {
266 HGLOBAL hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE | GMEM_ZEROINIT, sizeof(DWORD));
267 DWORD *pdwEffect = (DWORD *)GlobalLock(hData);
268 AssertPtr(pdwEffect);
269 *pdwEffect = DROPEFFECT_COPY;
270 GlobalUnlock(hData);
271
272 pMedium->hGlobal = hData;
273 pMedium->tymed = TYMED_HGLOBAL;
274 }
275 else
276#endif
277 if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
278 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
279 && (pFormatEtc->cfFormat == CF_TEXT))
280 {
281 pMedium->hGlobal = GlobalAlloc(GHND, mcbData + 1);
282 if (pMedium->hGlobal)
283 {
284 /** @todo Not working yet -- needs URI to plain ASCII conversion. */
285
286 char *pcDst = (char *)GlobalLock(pMedium->hGlobal);
287 memcpy(pcDst, mpvData, mcbData);
288 pcDst[mcbData] = '\0';
289 GlobalUnlock(pMedium->hGlobal);
290
291 hr = S_OK;
292 }
293 }
294 else if ( (pFormatEtc->tymed & TYMED_HGLOBAL)
295 && (pFormatEtc->dwAspect == DVASPECT_CONTENT)
296 && (pFormatEtc->cfFormat == CF_HDROP))
297 {
298 int rc = VINF_SUCCESS;
299
300 size_t cchFiles = 0; /* Number of ASCII characters. */
301 for (size_t i = 0; i < lstFiles.size(); i++)
302 {
303 cchFiles += strlen(lstFiles.at(i).c_str());
304 cchFiles += 1; /* Terminating '\0'. */
305 }
306
307 size_t cbBuf = sizeof(DROPFILES) + ((cchFiles + 1) * sizeof(RTUTF16));
308 DROPFILES *pBuf = (DROPFILES *)RTMemAllocZ(cbBuf);
309 if (pBuf)
310 {
311 pBuf->pFiles = sizeof(DROPFILES);
312 pBuf->fWide = 1; /* We use unicode. Always. */
313
314 uint8_t *pCurFile = (uint8_t *)pBuf + pBuf->pFiles;
315 AssertPtr(pCurFile);
316
317 for (size_t i = 0; i < lstFiles.size() && RT_SUCCESS(rc); i++)
318 {
319 size_t cchCurFile;
320 PRTUTF16 pwszFile;
321 rc = RTStrToUtf16(lstFiles.at(i).c_str(), &pwszFile);
322 if (RT_SUCCESS(rc))
323 {
324 cchCurFile = RTUtf16Len(pwszFile);
325 Assert(cchCurFile);
326 memcpy(pCurFile, pwszFile, cchCurFile * sizeof(RTUTF16));
327 RTUtf16Free(pwszFile);
328 }
329 else
330 break;
331
332 pCurFile += cchCurFile * sizeof(RTUTF16);
333
334 /* Terminate current file name. */
335 *pCurFile = L'\0';
336 pCurFile += sizeof(RTUTF16);
337 }
338
339 if (RT_SUCCESS(rc))
340 {
341 *pCurFile = L'\0'; /* Final list terminator. */
342
343 pMedium->tymed = TYMED_HGLOBAL;
344 pMedium->pUnkForRelease = NULL;
345 pMedium->hGlobal = GlobalAlloc( GMEM_ZEROINIT
346 | GMEM_MOVEABLE
347 | GMEM_DDESHARE, cbBuf);
348 if (pMedium->hGlobal)
349 {
350 LPVOID pMem = GlobalLock(pMedium->hGlobal);
351 if (pMem)
352 {
353 memcpy(pMem, pBuf, cbBuf);
354 GlobalUnlock(pMedium->hGlobal);
355
356 hr = S_OK;
357 }
358 }
359 }
360
361 RTMemFree(pBuf);
362 }
363 else
364 rc = VERR_NO_MEMORY;
365
366 if (RT_FAILURE(rc))
367 hr = DV_E_FORMATETC;
368 }
369 }
370 }
371
372 if (FAILED(hr))
373 {
374 LogFlowFunc(("Copying medium ...\n"));
375 switch (pThisMedium->tymed)
376 {
377
378 case TYMED_HGLOBAL:
379 pMedium->hGlobal = (HGLOBAL)OleDuplicateData(pThisMedium->hGlobal,
380 pThisFormat->cfFormat, NULL);
381 break;
382
383 default:
384 break;
385 }
386
387 pMedium->tymed = pThisFormat->tymed;
388 pMedium->pUnkForRelease = NULL;
389 }
390
391 LogFlowFunc(("hr=%Rhrc\n", hr));
392 return hr;
393}
394
395/**
396 * Only required for IStream / IStorage interfaces.
397 *
398 * @return IPRT status code.
399 * @return HRESULT
400 * @param pFormatEtc
401 * @param pMedium
402 */
403STDMETHODIMP VBoxDnDDataObject::GetDataHere(FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
404{
405 LogFlowFunc(("\n"));
406 return DATA_E_FORMATETC;
407}
408
409/**
410 * Query if this objects supports a specific format.
411 *
412 * @return IPRT status code.
413 * @return HRESULT
414 * @param pFormatEtc
415 */
416STDMETHODIMP VBoxDnDDataObject::QueryGetData(FORMATETC *pFormatEtc)
417{
418 LogFlowFunc(("\n"));
419 return (LookupFormatEtc(pFormatEtc, NULL /* puIndex */)) ? S_OK : DV_E_FORMATETC;
420}
421
422STDMETHODIMP VBoxDnDDataObject::GetCanonicalFormatEtc(FORMATETC *pFormatEct, FORMATETC *pFormatEtcOut)
423{
424 LogFlowFunc(("\n"));
425
426 /* Set this to NULL in any case. */
427 pFormatEtcOut->ptd = NULL;
428 return E_NOTIMPL;
429}
430
431STDMETHODIMP VBoxDnDDataObject::SetData(FORMATETC *pFormatEtc, STGMEDIUM *pMedium, BOOL fRelease)
432{
433 return E_NOTIMPL;
434}
435
436STDMETHODIMP VBoxDnDDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC **ppEnumFormatEtc)
437{
438 LogFlowFunc(("dwDirection=%RI32, mcFormats=%RI32, mpFormatEtc=%p\n",
439 dwDirection, mcFormats, mpFormatEtc));
440
441 HRESULT hr;
442 if (dwDirection == DATADIR_GET)
443 {
444 hr = VBoxDnDEnumFormatEtc::CreateEnumFormatEtc(mcFormats, mpFormatEtc, ppEnumFormatEtc);
445 }
446 else
447 hr = E_NOTIMPL;
448
449 LogFlowFunc(("hr=%Rhrc\n", hr));
450 return hr;
451}
452
453STDMETHODIMP VBoxDnDDataObject::DAdvise(FORMATETC *pFormatEtc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection)
454{
455 return OLE_E_ADVISENOTSUPPORTED;
456}
457
458STDMETHODIMP VBoxDnDDataObject::DUnadvise(DWORD dwConnection)
459{
460 return OLE_E_ADVISENOTSUPPORTED;
461}
462
463STDMETHODIMP VBoxDnDDataObject::EnumDAdvise(IEnumSTATDATA **ppEnumAdvise)
464{
465 return OLE_E_ADVISENOTSUPPORTED;
466}
467
468/*
469 * Own stuff.
470 */
471
472int VBoxDnDDataObject::Abort(void)
473{
474 LogFlowFunc(("Aborting ...\n"));
475 mStatus = Aborted;
476 return RTSemEventSignal(mSemEvent);
477}
478
479/* static */
480const char* VBoxDnDDataObject::ClipboardFormatToString(CLIPFORMAT fmt)
481{
482#ifdef VBOX_DND_DEBUG_FORMATS
483 char szFormat[128];
484 if (GetClipboardFormatName(fmt, szFormat, sizeof(szFormat)))
485 LogFlowFunc(("wFormat=%RI16, szName=%s\n", fmt, szFormat));
486#endif
487
488 switch (fmt)
489 {
490
491 case 1:
492 return "CF_TEXT";
493 case 2:
494 return "CF_BITMAP";
495 case 3:
496 return "CF_METAFILEPICT";
497 case 4:
498 return "CF_SYLK";
499 case 5:
500 return "CF_DIF";
501 case 6:
502 return "CF_TIFF";
503 case 7:
504 return "CF_OEMTEXT";
505 case 8:
506 return "CF_DIB";
507 case 9:
508 return "CF_PALETTE";
509 case 10:
510 return "CF_PENDATA";
511 case 11:
512 return "CF_RIFF";
513 case 12:
514 return "CF_WAVE";
515 case 13:
516 return "CF_UNICODETEXT";
517 case 14:
518 return "CF_ENHMETAFILE";
519 case 15:
520 return "CF_HDROP";
521 case 16:
522 return "CF_LOCALE";
523 case 17:
524 return "CF_DIBV5";
525 case 18:
526 return "CF_MAX";
527 case 49158:
528 return "FileName";
529 case 49159:
530 return "FileNameW";
531 case 49161:
532 return "DATAOBJECT";
533 case 49171:
534 return "Ole Private Data";
535 case 49314:
536 return "Shell Object Offsets";
537 case 49316:
538 return "File Contents";
539 case 49317:
540 return "File Group Descriptor";
541 case 49323:
542 return "Preferred Drop Effect";
543 case 49380:
544 return "Shell Object Offsets";
545 case 49382:
546 return "FileContents";
547 case 49383:
548 return "FileGroupDescriptor";
549 case 49389:
550 return "Preferred DropEffect";
551 case 49268:
552 return "Shell IDList Array";
553 case 49619:
554 return "RenPrivateFileAttachments";
555 default:
556 break;
557 }
558
559 return "unknown";
560}
561
562bool VBoxDnDDataObject::LookupFormatEtc(FORMATETC *pFormatEtc, ULONG *puIndex)
563{
564 AssertReturn(pFormatEtc, false);
565 /* puIndex is optional. */
566
567 for (ULONG i = 0; i < mcFormats; i++)
568 {
569 if( (pFormatEtc->tymed & mpFormatEtc[i].tymed)
570 && pFormatEtc->cfFormat == mpFormatEtc[i].cfFormat
571 && pFormatEtc->dwAspect == mpFormatEtc[i].dwAspect)
572 {
573 LogFlowFunc(("Format found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32, ulIndex=%RU32\n",
574 pFormatEtc->tymed, pFormatEtc->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(mpFormatEtc[i].cfFormat),
575 pFormatEtc->dwAspect, i));
576 if (puIndex)
577 *puIndex = i;
578 return true;
579 }
580 }
581
582#ifdef VBOX_DND_DEBUG_FORMATS
583 LogFlowFunc(("Format NOT found: tyMed=%RI32, cfFormat=%RI16, sFormats=%s, dwAspect=%RI32\n",
584 pFormatEtc->tymed, pFormatEtc->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat),
585 pFormatEtc->dwAspect));
586#endif
587 return false;
588}
589
590/* static */
591HGLOBAL VBoxDnDDataObject::MemDup(HGLOBAL hMemSource)
592{
593 DWORD dwLen = GlobalSize(hMemSource);
594 AssertReturn(dwLen, NULL);
595 PVOID pvSource = GlobalLock(hMemSource);
596 if (pvSource)
597 {
598 PVOID pvDest = GlobalAlloc(GMEM_FIXED, dwLen);
599 if (pvDest)
600 memcpy(pvDest, pvSource, dwLen);
601
602 GlobalUnlock(hMemSource);
603 return pvDest;
604 }
605
606 return NULL;
607}
608
609void VBoxDnDDataObject::RegisterFormat(FORMATETC *pFormatEtc, CLIPFORMAT clipFormat,
610 TYMED tyMed, LONG lIndex, DWORD dwAspect,
611 DVTARGETDEVICE *pTargetDevice)
612{
613 AssertPtr(pFormatEtc);
614
615 pFormatEtc->cfFormat = clipFormat;
616 pFormatEtc->tymed = tyMed;
617 pFormatEtc->lindex = lIndex;
618 pFormatEtc->dwAspect = dwAspect;
619 pFormatEtc->ptd = pTargetDevice;
620
621 LogFlowFunc(("Registered format=%ld, sFormat=%s\n",
622 pFormatEtc->cfFormat, VBoxDnDDataObject::ClipboardFormatToString(pFormatEtc->cfFormat)));
623}
624
625void VBoxDnDDataObject::SetStatus(Status status)
626{
627 LogFlowFunc(("Setting status to %ld\n", status));
628 mStatus = status;
629}
630
631int VBoxDnDDataObject::Signal(const RTCString &strFormat,
632 const void *pvData, uint32_t cbData)
633{
634 LogFlowFunc(("Signalling ...\n"));
635
636 int rc;
637
638 mStatus = Dropped;
639 mstrFormat = strFormat;
640 if (cbData)
641 {
642 mpvData = RTMemAlloc(cbData);
643 if (mpvData)
644 {
645 memcpy(mpvData, pvData, cbData);
646 mcbData = cbData;
647 rc = VINF_SUCCESS;
648 }
649 else
650 rc = VERR_NO_MEMORY;
651 }
652 else
653 rc = VINF_SUCCESS;
654
655 if (RT_FAILURE(rc))
656 mStatus = Aborted;
657
658 /* Signal in any case. */
659 int rc2 = RTSemEventSignal(mSemEvent);
660 if (RT_SUCCESS(rc))
661 rc = rc2;
662
663 return rc;
664}
665
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