001/** 002 * 003 * Copyright © 2016-2017 Florian Schmaus, Fernando Ramirez 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.mam; 018 019import java.util.ArrayList; 020import java.util.Date; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.UUID; 025import java.util.WeakHashMap; 026 027import org.jivesoftware.smack.ConnectionCreationListener; 028import org.jivesoftware.smack.Manager; 029import org.jivesoftware.smack.SmackException.NoResponseException; 030import org.jivesoftware.smack.SmackException.NotConnectedException; 031import org.jivesoftware.smack.SmackException.NotLoggedInException; 032import org.jivesoftware.smack.StanzaCollector; 033import org.jivesoftware.smack.XMPPConnection; 034import org.jivesoftware.smack.XMPPConnectionRegistry; 035import org.jivesoftware.smack.XMPPException.XMPPErrorException; 036import org.jivesoftware.smack.filter.IQReplyFilter; 037import org.jivesoftware.smack.packet.IQ; 038import org.jivesoftware.smack.packet.Message; 039import org.jivesoftware.smack.util.Objects; 040 041import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 042import org.jivesoftware.smackx.forward.packet.Forwarded; 043import org.jivesoftware.smackx.mam.element.MamElements; 044import org.jivesoftware.smackx.mam.element.MamFinIQ; 045import org.jivesoftware.smackx.mam.element.MamPrefsIQ; 046import org.jivesoftware.smackx.mam.element.MamPrefsIQ.DefaultBehavior; 047import org.jivesoftware.smackx.mam.element.MamQueryIQ; 048import org.jivesoftware.smackx.mam.filter.MamResultFilter; 049import org.jivesoftware.smackx.rsm.packet.RSMSet; 050import org.jivesoftware.smackx.xdata.FormField; 051import org.jivesoftware.smackx.xdata.packet.DataForm; 052 053import org.jxmpp.jid.BareJid; 054import org.jxmpp.jid.EntityBareJid; 055import org.jxmpp.jid.EntityFullJid; 056import org.jxmpp.jid.Jid; 057import org.jxmpp.util.XmppDateTime; 058 059/** 060 * A Manager for Message Archive Management (XEP-0313). 061 * 062 * @see <a href="http://xmpp.org/extensions/xep-0313.html">XEP-0313: Message 063 * Archive Management</a> 064 * @author Florian Schmaus 065 * @author Fernando Ramirez 066 * 067 */ 068public final class MamManager extends Manager { 069 070 static { 071 XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() { 072 @Override 073 public void connectionCreated(XMPPConnection connection) { 074 getInstanceFor(connection); 075 } 076 }); 077 } 078 079 private static final Map<XMPPConnection, Map<Jid, MamManager>> INSTANCES = new WeakHashMap<>(); 080 081 /** 082 * Get the singleton instance of MamManager. 083 * 084 * @param connection 085 * @return the instance of MamManager 086 */ 087 public static MamManager getInstanceFor(XMPPConnection connection) { 088 return getInstanceFor(connection, null); 089 } 090 091 public static synchronized MamManager getInstanceFor(XMPPConnection connection, Jid archiveAddress) { 092 Map<Jid, MamManager> managers = INSTANCES.get(connection); 093 if (managers == null) { 094 managers = new HashMap<>(); 095 INSTANCES.put(connection, managers); 096 } 097 MamManager mamManager = managers.get(archiveAddress); 098 if (mamManager == null) { 099 mamManager = new MamManager(connection, archiveAddress); 100 managers.put(archiveAddress, mamManager); 101 } 102 return mamManager; 103 } 104 105 private final Jid archiveAddress; 106 107 private final ServiceDiscoveryManager serviceDiscoveryManager; 108 109 private MamManager(XMPPConnection connection, Jid archiveAddress) { 110 super(connection); 111 this.archiveAddress = archiveAddress; 112 serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); 113 } 114 115 /** 116 * Query archive with a maximum amount of results. 117 * 118 * @param max 119 * @return the MAM query result 120 * @throws NoResponseException 121 * @throws XMPPErrorException 122 * @throws NotConnectedException 123 * @throws InterruptedException 124 * @throws NotLoggedInException 125 */ 126 public MamQueryResult queryArchive(Integer max) throws NoResponseException, XMPPErrorException, 127 NotConnectedException, InterruptedException, NotLoggedInException { 128 return queryArchive(null, max, null, null, null, null); 129 } 130 131 /** 132 * Query archive with a JID (only messages from/to the JID). 133 * 134 * @param withJid 135 * @return the MAM query result 136 * @throws NoResponseException 137 * @throws XMPPErrorException 138 * @throws NotConnectedException 139 * @throws InterruptedException 140 * @throws NotLoggedInException 141 */ 142 public MamQueryResult queryArchive(Jid withJid) throws NoResponseException, XMPPErrorException, 143 NotConnectedException, InterruptedException, NotLoggedInException { 144 return queryArchive(null, null, null, null, withJid, null); 145 } 146 147 /** 148 * Query archive filtering by start and/or end date. If start == null, the 149 * value of 'start' will be equal to the date/time of the earliest message 150 * stored in the archive. If end == null, the value of 'end' will be equal 151 * to the date/time of the most recent message stored in the archive. 152 * 153 * @param start 154 * @param end 155 * @return the MAM query result 156 * @throws NoResponseException 157 * @throws XMPPErrorException 158 * @throws NotConnectedException 159 * @throws InterruptedException 160 * @throws NotLoggedInException 161 */ 162 public MamQueryResult queryArchive(Date start, Date end) throws NoResponseException, XMPPErrorException, 163 NotConnectedException, InterruptedException, NotLoggedInException { 164 return queryArchive(null, null, start, end, null, null); 165 } 166 167 /** 168 * Query Archive adding filters with additional fields. 169 * 170 * @param additionalFields 171 * @return the MAM query result 172 * @throws NoResponseException 173 * @throws XMPPErrorException 174 * @throws NotConnectedException 175 * @throws InterruptedException 176 * @throws NotLoggedInException 177 */ 178 public MamQueryResult queryArchive(List<FormField> additionalFields) throws NoResponseException, XMPPErrorException, 179 NotConnectedException, InterruptedException, NotLoggedInException { 180 return queryArchive(null, null, null, null, null, additionalFields); 181 } 182 183 /** 184 * Query archive filtering by start date. The value of 'end' will be equal 185 * to the date/time of the most recent message stored in the archive. 186 * 187 * @param start 188 * @return the MAM query result 189 * @throws NoResponseException 190 * @throws XMPPErrorException 191 * @throws NotConnectedException 192 * @throws InterruptedException 193 * @throws NotLoggedInException 194 */ 195 public MamQueryResult queryArchiveWithStartDate(Date start) throws NoResponseException, XMPPErrorException, 196 NotConnectedException, InterruptedException, NotLoggedInException { 197 return queryArchive(null, null, start, null, null, null); 198 } 199 200 /** 201 * Query archive filtering by end date. The value of 'start' will be equal 202 * to the date/time of the earliest message stored in the archive. 203 * 204 * @param end 205 * @return the MAM query result 206 * @throws NoResponseException 207 * @throws XMPPErrorException 208 * @throws NotConnectedException 209 * @throws InterruptedException 210 * @throws NotLoggedInException 211 */ 212 public MamQueryResult queryArchiveWithEndDate(Date end) throws NoResponseException, XMPPErrorException, 213 NotConnectedException, InterruptedException, NotLoggedInException { 214 return queryArchive(null, null, null, end, null, null); 215 } 216 217 218 /** 219 * Query archive applying filters: max count, start date, end date, from/to 220 * JID and with additional fields. 221 * 222 * @param max 223 * @param start 224 * @param end 225 * @param withJid 226 * @param additionalFields 227 * @return the MAM query result 228 * @throws NoResponseException 229 * @throws XMPPErrorException 230 * @throws NotConnectedException 231 * @throws InterruptedException 232 * @throws NotLoggedInException 233 */ 234 public MamQueryResult queryArchive(Integer max, Date start, Date end, Jid withJid, List<FormField> additionalFields) 235 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, 236 NotLoggedInException { 237 return queryArchive(null, max, start, end, withJid, additionalFields); 238 } 239 240 241 /** 242 * Query an message archive like a MUC archive or a PubSub node archive, addressed by an archiveAddress, applying 243 * filters: max count, start date, end date, from/to JID and with additional fields. When archiveAddress is null the 244 * default, the server will be requested. 245 * 246 * @param node The PubSub node name, can be null 247 * @param max 248 * @param start 249 * @param end 250 * @param withJid 251 * @param additionalFields 252 * @return the MAM query result 253 * @throws NoResponseException 254 * @throws XMPPErrorException 255 * @throws NotConnectedException 256 * @throws InterruptedException 257 * @throws NotLoggedInException 258 */ 259 public MamQueryResult queryArchive(String node, Integer max, Date start, Date end, Jid withJid, 260 List<FormField> additionalFields) 261 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, 262 NotLoggedInException { 263 DataForm dataForm = null; 264 String queryId = UUID.randomUUID().toString(); 265 266 if (start != null || end != null || withJid != null || additionalFields != null) { 267 dataForm = getNewMamForm(); 268 addStart(start, dataForm); 269 addEnd(end, dataForm); 270 addWithJid(withJid, dataForm); 271 addAdditionalFields(additionalFields, dataForm); 272 } 273 274 MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, dataForm); 275 mamQueryIQ.setType(IQ.Type.set); 276 mamQueryIQ.setTo(archiveAddress); 277 278 addResultsLimit(max, mamQueryIQ); 279 return queryArchive(mamQueryIQ); 280 } 281 282 private static void addAdditionalFields(List<FormField> additionalFields, DataForm dataForm) { 283 if (additionalFields == null) { 284 return; 285 } 286 for (FormField formField : additionalFields) { 287 dataForm.addField(formField); 288 } 289 } 290 291 private static void addResultsLimit(Integer max, MamQueryIQ mamQueryIQ) { 292 if (max == null) { 293 return; 294 } 295 RSMSet rsmSet = new RSMSet(max); 296 mamQueryIQ.addExtension(rsmSet); 297 298 } 299 300 private static void addWithJid(Jid withJid, DataForm dataForm) { 301 if (withJid == null) { 302 return; 303 } 304 FormField formField = new FormField("with"); 305 formField.addValue(withJid.toString()); 306 dataForm.addField(formField); 307 } 308 309 private static void addEnd(Date end, DataForm dataForm) { 310 if (end == null) { 311 return; 312 } 313 FormField formField = new FormField("end"); 314 formField.addValue(XmppDateTime.formatXEP0082Date(end)); 315 dataForm.addField(formField); 316 } 317 318 private static void addStart(Date start, DataForm dataForm) { 319 if (start == null) { 320 return; 321 } 322 FormField formField = new FormField("start"); 323 formField.addValue(XmppDateTime.formatXEP0082Date(start)); 324 dataForm.addField(formField); 325 } 326 327 /** 328 * Returns a page of the archive. 329 * 330 * @param dataForm 331 * @param rsmSet 332 * @return the MAM query result 333 * @throws NoResponseException 334 * @throws XMPPErrorException 335 * @throws NotConnectedException 336 * @throws InterruptedException 337 * @throws NotLoggedInException 338 */ 339 public MamQueryResult page(DataForm dataForm, RSMSet rsmSet) throws NoResponseException, XMPPErrorException, 340 NotConnectedException, InterruptedException, NotLoggedInException { 341 342 return page(null, dataForm, rsmSet); 343 344 } 345 346 /** 347 * Returns a page of the archive. 348 * 349 * @param node The PubSub node name, can be null 350 * @param dataForm 351 * @param rsmSet 352 * @return the MAM query result 353 * @throws NoResponseException 354 * @throws XMPPErrorException 355 * @throws NotConnectedException 356 * @throws InterruptedException 357 * @throws NotLoggedInException 358 */ 359 public MamQueryResult page(String node, DataForm dataForm, RSMSet rsmSet) 360 throws NoResponseException, XMPPErrorException, 361 NotConnectedException, InterruptedException, NotLoggedInException { 362 MamQueryIQ mamQueryIQ = new MamQueryIQ(UUID.randomUUID().toString(), node, dataForm); 363 mamQueryIQ.setType(IQ.Type.set); 364 mamQueryIQ.setTo(archiveAddress); 365 mamQueryIQ.addExtension(rsmSet); 366 return queryArchive(mamQueryIQ); 367 } 368 369 /** 370 * Returns the next page of the archive. 371 * 372 * @param mamQueryResult 373 * is the previous query result 374 * @param count 375 * is the amount of messages that a page contains 376 * @return the MAM query result 377 * @throws NoResponseException 378 * @throws XMPPErrorException 379 * @throws NotConnectedException 380 * @throws InterruptedException 381 * @throws NotLoggedInException 382 */ 383 public MamQueryResult pageNext(MamQueryResult mamQueryResult, int count) throws NoResponseException, 384 XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { 385 RSMSet previousResultRsmSet = mamQueryResult.mamFin.getRSMSet(); 386 RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getLast(), RSMSet.PageDirection.after); 387 return page(mamQueryResult, requestRsmSet); 388 } 389 390 /** 391 * Returns the previous page of the archive. 392 * 393 * @param mamQueryResult 394 * is the previous query result 395 * @param count 396 * is the amount of messages that a page contains 397 * @return the MAM query result 398 * @throws NoResponseException 399 * @throws XMPPErrorException 400 * @throws NotConnectedException 401 * @throws InterruptedException 402 * @throws NotLoggedInException 403 */ 404 public MamQueryResult pagePrevious(MamQueryResult mamQueryResult, int count) throws NoResponseException, 405 XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException { 406 RSMSet previousResultRsmSet = mamQueryResult.mamFin.getRSMSet(); 407 RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getFirst(), RSMSet.PageDirection.before); 408 return page(mamQueryResult, requestRsmSet); 409 } 410 411 private MamQueryResult page(MamQueryResult mamQueryResult, RSMSet requestRsmSet) throws NoResponseException, 412 XMPPErrorException, NotConnectedException, NotLoggedInException, InterruptedException { 413 ensureMamQueryResultMatchesThisManager(mamQueryResult); 414 415 return page(mamQueryResult.node, mamQueryResult.form, requestRsmSet); 416 } 417 418 /** 419 * Obtain page before the first message saved (specific chat). 420 * <p> 421 * Note that the messageUid is the XEP-0313 UID and <b>not</b> the stanza ID of the message. 422 * </p> 423 * 424 * @param chatJid 425 * @param messageUid the UID of the message of which messages before should be received. 426 * @param max 427 * @return the MAM query result 428 * @throws XMPPErrorException 429 * @throws NotLoggedInException 430 * @throws NotConnectedException 431 * @throws InterruptedException 432 * @throws NoResponseException 433 */ 434 public MamQueryResult pageBefore(Jid chatJid, String messageUid, int max) throws XMPPErrorException, 435 NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException { 436 RSMSet rsmSet = new RSMSet(null, messageUid, -1, -1, null, max, null, -1); 437 DataForm dataForm = getNewMamForm(); 438 addWithJid(chatJid, dataForm); 439 return page(null, dataForm, rsmSet); 440 } 441 442 /** 443 * Obtain page after the last message saved (specific chat). 444 * <p> 445 * Note that the messageUid is the XEP-0313 UID and <b>not</b> the stanza ID of the message. 446 * </p> 447 * 448 * @param chatJid 449 * @param messageUid the UID of the message of which messages after should be received. 450 * @param max 451 * @return the MAM query result 452 * @throws XMPPErrorException 453 * @throws NotLoggedInException 454 * @throws NotConnectedException 455 * @throws InterruptedException 456 * @throws NoResponseException 457 */ 458 public MamQueryResult pageAfter(Jid chatJid, String messageUid, int max) throws XMPPErrorException, 459 NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException { 460 RSMSet rsmSet = new RSMSet(messageUid, null, -1, -1, null, max, null, -1); 461 DataForm dataForm = getNewMamForm(); 462 addWithJid(chatJid, dataForm); 463 return page(null, dataForm, rsmSet); 464 } 465 466 /** 467 * Obtain the most recent page of a chat. 468 * 469 * @param chatJid 470 * @param max 471 * @return the MAM query result 472 * @throws XMPPErrorException 473 * @throws NotLoggedInException 474 * @throws NotConnectedException 475 * @throws InterruptedException 476 * @throws NoResponseException 477 */ 478 public MamQueryResult mostRecentPage(Jid chatJid, int max) throws XMPPErrorException, NotLoggedInException, 479 NotConnectedException, InterruptedException, NoResponseException { 480 return pageBefore(chatJid, "", max); 481 } 482 483 /** 484 * Get the form fields supported by the server. 485 * 486 * @return the list of form fields. 487 * @throws NoResponseException 488 * @throws XMPPErrorException 489 * @throws NotConnectedException 490 * @throws InterruptedException 491 * @throws NotLoggedInException 492 */ 493 public List<FormField> retrieveFormFields() throws NoResponseException, XMPPErrorException, NotConnectedException, 494 InterruptedException, NotLoggedInException { 495 return retrieveFormFields(null); 496 } 497 498 /** 499 * Get the form fields supported by the server. 500 * 501 * @param node The PubSub node name, can be null 502 * @return the list of form fields. 503 * @throws NoResponseException 504 * @throws XMPPErrorException 505 * @throws NotConnectedException 506 * @throws InterruptedException 507 * @throws NotLoggedInException 508 */ 509 public List<FormField> retrieveFormFields(String node) 510 throws NoResponseException, XMPPErrorException, NotConnectedException, 511 InterruptedException, NotLoggedInException { 512 String queryId = UUID.randomUUID().toString(); 513 MamQueryIQ mamQueryIq = new MamQueryIQ(queryId, node, null); 514 mamQueryIq.setTo(archiveAddress); 515 516 MamQueryIQ mamResponseQueryIq = connection().createStanzaCollectorAndSend(mamQueryIq).nextResultOrThrow(); 517 518 return mamResponseQueryIq.getDataForm().getFields(); 519 } 520 521 private MamQueryResult queryArchive(MamQueryIQ mamQueryIq) throws NoResponseException, XMPPErrorException, 522 NotConnectedException, InterruptedException, NotLoggedInException { 523 final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); 524 MamFinIQ mamFinIQ; 525 526 StanzaCollector mamFinIQCollector = connection.createStanzaCollector(new IQReplyFilter(mamQueryIq, connection)); 527 528 StanzaCollector.Configuration resultCollectorConfiguration = StanzaCollector.newConfiguration() 529 .setStanzaFilter(new MamResultFilter(mamQueryIq)).setCollectorToReset(mamFinIQCollector); 530 StanzaCollector resultCollector = connection.createStanzaCollector(resultCollectorConfiguration); 531 532 try { 533 connection.sendStanza(mamQueryIq); 534 mamFinIQ = mamFinIQCollector.nextResultOrThrow(); 535 } finally { 536 mamFinIQCollector.cancel(); 537 resultCollector.cancel(); 538 } 539 540 List<Forwarded> forwardedMessages = new ArrayList<>(resultCollector.getCollectedCount()); 541 542 for (Message resultMessage = resultCollector 543 .pollResult(); resultMessage != null; resultMessage = resultCollector.pollResult()) { 544 MamElements.MamResultExtension mamResultExtension = MamElements.MamResultExtension.from(resultMessage); 545 forwardedMessages.add(mamResultExtension.getForwarded()); 546 } 547 548 return new MamQueryResult(forwardedMessages, mamFinIQ, mamQueryIq.getNode(), DataForm.from(mamQueryIq)); 549 } 550 551 /** 552 * MAM query result class. 553 * 554 */ 555 public final static class MamQueryResult { 556 public final List<Forwarded> forwardedMessages; 557 public final MamFinIQ mamFin; 558 private final String node; 559 private final DataForm form; 560 561 private MamQueryResult(List<Forwarded> forwardedMessages, MamFinIQ mamFin, String node, DataForm form) { 562 this.forwardedMessages = forwardedMessages; 563 this.mamFin = mamFin; 564 this.node = node; 565 this.form = form; 566 } 567 } 568 569 private void ensureMamQueryResultMatchesThisManager(MamQueryResult mamQueryResult) { 570 EntityFullJid localAddress = connection().getUser(); 571 EntityBareJid localBareAddress = null; 572 if (localAddress != null) { 573 localBareAddress = localAddress.asEntityBareJid(); 574 } 575 boolean isLocalUserArchive = archiveAddress == null || archiveAddress.equals(localBareAddress); 576 577 Jid finIqFrom = mamQueryResult.mamFin.getFrom(); 578 579 if (finIqFrom != null) { 580 if (finIqFrom.equals(archiveAddress) || (isLocalUserArchive && finIqFrom.equals(localBareAddress))) { 581 return; 582 } 583 throw new IllegalArgumentException("The given MamQueryResult is from the MAM archive '" + finIqFrom 584 + "' whereas this MamManager is responsible for '" + archiveAddress + '\''); 585 } 586 else if (!isLocalUserArchive) { 587 throw new IllegalArgumentException( 588 "The given MamQueryResult is from the local entity (user) MAM archive, whereas this MamManager is responsible for '" 589 + archiveAddress + '\''); 590 } 591 } 592 593 /** 594 * Check if MAM is supported for the XMPP connection managed by this MamManager. 595 * 596 * @return true if MAM is supported for the XMPP connection, <code>false</code>otherwhise. 597 * 598 * @throws NoResponseException 599 * @throws XMPPErrorException 600 * @throws NotConnectedException 601 * @throws InterruptedException 602 * @since 4.2.1 603 * @see <a href="https://xmpp.org/extensions/xep-0313.html#support">XEP-0313 § 7. Determining support</a> 604 */ 605 public boolean isSupported() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 606 BareJid myBareJid = connection().getUser().asBareJid(); 607 return serviceDiscoveryManager.supportsFeature(myBareJid, MamElements.NAMESPACE); 608 } 609 610 /** 611 * Returns true if Message Archive Management is supported by the server. 612 * 613 * @return true if Message Archive Management is supported by the server. 614 * @throws NotConnectedException 615 * @throws XMPPErrorException 616 * @throws NoResponseException 617 * @throws InterruptedException 618 * @deprecated use {@link #isSupported()} instead. 619 */ 620 @Deprecated 621 // TODO Remove in Smack 4.3 622 public boolean isSupportedByServer() 623 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 624 return ServiceDiscoveryManager.getInstanceFor(connection()).serverSupportsFeature(MamElements.NAMESPACE); 625 } 626 627 private static DataForm getNewMamForm() { 628 FormField field = new FormField(FormField.FORM_TYPE); 629 field.setType(FormField.Type.hidden); 630 field.addValue(MamElements.NAMESPACE); 631 DataForm form = new DataForm(DataForm.Type.submit); 632 form.addField(field); 633 return form; 634 } 635 636 /** 637 * Get the preferences stored in the server. 638 * 639 * @return the MAM preferences result 640 * @throws NoResponseException 641 * @throws XMPPErrorException 642 * @throws NotConnectedException 643 * @throws InterruptedException 644 * @throws NotLoggedInException 645 */ 646 public MamPrefsResult retrieveArchivingPreferences() throws NoResponseException, XMPPErrorException, 647 NotConnectedException, InterruptedException, NotLoggedInException { 648 MamPrefsIQ mamPrefIQ = new MamPrefsIQ(); 649 return queryMamPrefs(mamPrefIQ); 650 } 651 652 /** 653 * Update the preferences in the server. 654 * 655 * @param alwaysJids 656 * is the list of JIDs that should always have messages to/from 657 * archived in the user's store 658 * @param neverJids 659 * is the list of JIDs that should never have messages to/from 660 * archived in the user's store 661 * @param defaultBehavior 662 * can be "roster", "always", "never" (see XEP-0313) 663 * @return the MAM preferences result 664 * @throws NoResponseException 665 * @throws XMPPErrorException 666 * @throws NotConnectedException 667 * @throws InterruptedException 668 * @throws NotLoggedInException 669 */ 670 public MamPrefsResult updateArchivingPreferences(List<Jid> alwaysJids, List<Jid> neverJids, DefaultBehavior defaultBehavior) 671 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, 672 NotLoggedInException { 673 Objects.requireNonNull(defaultBehavior, "Default behavior must be set"); 674 MamPrefsIQ mamPrefIQ = new MamPrefsIQ(alwaysJids, neverJids, defaultBehavior); 675 return queryMamPrefs(mamPrefIQ); 676 } 677 678 /** 679 * MAM preferences result class. 680 * 681 */ 682 public final static class MamPrefsResult { 683 public final MamPrefsIQ mamPrefs; 684 public final DataForm form; 685 686 private MamPrefsResult(MamPrefsIQ mamPrefs, DataForm form) { 687 this.mamPrefs = mamPrefs; 688 this.form = form; 689 } 690 } 691 692 private MamPrefsResult queryMamPrefs(MamPrefsIQ mamPrefsIQ) throws NoResponseException, XMPPErrorException, 693 NotConnectedException, InterruptedException, NotLoggedInException { 694 final XMPPConnection connection = getAuthenticatedConnectionOrThrow(); 695 696 MamPrefsIQ mamPrefsResultIQ = connection.createStanzaCollectorAndSend(mamPrefsIQ).nextResultOrThrow(); 697 698 return new MamPrefsResult(mamPrefsResultIQ, DataForm.from(mamPrefsIQ)); 699 } 700 701}