1 /*
2  * Copyright (C) 2004-2008 Jive Software. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.jivesoftware.util;
18 
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.net.URL;
25 import java.util.Arrays;
26 import java.util.Comparator;
27 import java.util.Date;
28 import java.util.StringTokenizer;
29 
30 import org.jivesoftware.openfire.PresenceManager;
31 import org.jivesoftware.openfire.PrivateStorage;
32 import org.jivesoftware.openfire.SessionManager;
33 import org.jivesoftware.openfire.XMPPServer;
34 import org.jivesoftware.openfire.XMPPServerInfo;
35 import org.jivesoftware.openfire.auth.AuthToken;
36 import org.jivesoftware.openfire.group.GroupManager;
37 import org.jivesoftware.openfire.lockout.LockOutManager;
38 import org.jivesoftware.openfire.muc.MultiUserChatManager;
39 import org.jivesoftware.openfire.pubsub.PubSubInfo;
40 import org.jivesoftware.openfire.pubsub.PubSubServiceInfo;
41 import org.jivesoftware.openfire.roster.RosterManager;
42 import org.jivesoftware.openfire.security.SecurityAuditManager;
43 import org.jivesoftware.openfire.user.User;
44 import org.jivesoftware.openfire.user.UserManager;
45 import org.jivesoftware.util.cache.Cache;
46 import org.jivesoftware.util.cache.CacheFactory;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 
50 import javax.servlet.http.HttpSession;
51 
52 /**
53  * A utility bean for Openfire admin console pages.
54  */
55 public class WebManager extends WebBean {
56 
57     private static final Logger Log = LoggerFactory.getLogger(WebManager.class);
58 
59     private int start = 0;
60     private int range = 15;
61 
WebManager()62     public WebManager() {
63     }
64 
65     /**
66      * Invalidates and recreates session (do this on login/logout).
67      * @return the new HTTP session
68      */
invalidateSession()69     public HttpSession invalidateSession() {
70         session.invalidate();
71         session = request.getSession(true);
72         return session;
73     }
74 
75     /**
76      * @return the auth token; redirect to the login page if an auth token is not found.
77      */
getAuthToken()78     public AuthToken getAuthToken() {
79         return session == null ? null : (AuthToken)session.getAttribute("jive.admin.authToken");
80     }
81 
82     /**
83      * @return {@code true} if the Openfire container is in setup mode, {@code false} otherwise.
84      */
isSetupMode()85     public boolean isSetupMode() {
86         return getXMPPServer().isSetupMode();
87     }
88 
89     /**
90      * @return the XMPP server object -- can get many config items from here.
91      */
getXMPPServer()92     public XMPPServer getXMPPServer() {
93         final XMPPServer xmppServer = XMPPServer.getInstance();
94         if (xmppServer == null) {
95             // Show that the server is down
96             showServerDown();
97             return null;
98         }
99         return xmppServer;
100     }
101 
getUserManager()102     public UserManager getUserManager() {
103         return getXMPPServer().getUserManager();
104     }
105 
getGroupManager()106     public GroupManager getGroupManager() {
107         return GroupManager.getInstance();
108     }
109 
getLockOutManager()110     public LockOutManager getLockOutManager() {
111         return LockOutManager.getInstance();
112     }
113 
getSecurityAuditManager()114     public SecurityAuditManager getSecurityAuditManager() {
115         return SecurityAuditManager.getInstance();
116     }
117 
getRosterManager()118     public RosterManager getRosterManager() {
119         return getXMPPServer().getRosterManager();
120     }
121 
getPrivateStore()122     public PrivateStorage getPrivateStore() {
123         return getXMPPServer().getPrivateStorage();
124     }
125 
getPresenceManager()126     public PresenceManager getPresenceManager() {
127         return getXMPPServer().getPresenceManager();
128     }
129 
getSessionManager()130     public SessionManager getSessionManager() {
131         return getXMPPServer().getSessionManager();
132     }
133 
getMultiUserChatManager()134     public MultiUserChatManager getMultiUserChatManager() {
135         return getXMPPServer().getMultiUserChatManager();
136     }
137 
getServerInfo()138     public XMPPServerInfo getServerInfo() {
139         return getXMPPServer().getServerInfo();
140     }
141 
getPubSubInfo()142     public PubSubServiceInfo getPubSubInfo() {
143         return new PubSubInfo();
144     }
145 
146     /**
147      * Logs a security event as the currently logged in user.  (convenience routine for SecurityAuditManager)
148      *
149      * @param summary Summary of event.
150      * @param details Details of event, can be null if no details available.
151      */
logEvent(String summary, String details)152     public void logEvent(String summary, String details) {
153         SecurityAuditManager.getInstance().logEvent(getUser().getUsername(), summary, details);
154     }
155 
156     /**
157      * @return the page user or {@code null} if one is not found.
158      */
getUser()159     public User getUser() {
160         User pageUser = null;
161         try {
162             final AuthToken authToken = getAuthToken();
163             if (authToken == null)
164             {
165                 Log.debug( "Unable to get user: no session or no auth token on session." );
166                 return null;
167             }
168             if (authToken instanceof AuthToken.OneTimeAuthToken) {
169                 return new User(authToken.getUsername(), "Recovery via One Time Auth Token", null, new Date(), new Date());
170             }
171             final String username = authToken.getUsername();
172             if (username == null || username.isEmpty())
173             {
174                 Log.debug( "Unable to get user: no username in auth token on session." );
175                 return null;
176             }
177             pageUser = getUserManager().getUser(username);
178         }
179         catch (Exception ex) {
180             Log.debug("Unexpected exception (which is ignored) while trying to obtain user.", ex);
181         }
182         return pageUser;
183     }
184 
185     /**
186      * @return {@code true} if the server is in embedded mode, {@code false} otherwise.
187      */
isEmbedded()188     public boolean isEmbedded() {
189         try {
190             ClassUtils.forName("org.jivesoftware.openfire.starter.ServerStarter");
191             return true;
192         }
193         catch (Exception ignored) {
194             return false;
195         }
196     }
197 
198     /**
199      * Restarts the server then sleeps for 3 seconds.
200      */
restart()201     public void restart() {
202         try {
203             getXMPPServer().restart();
204         }
205         catch (Exception e) {
206             Log.error(e.getMessage(), e);
207         }
208         sleep();
209     }
210 
211     /**
212      * Stops the server then sleeps for 3 seconds.
213      */
stop()214     public void stop() {
215         try {
216             getXMPPServer().stop();
217         }
218         catch (Exception e) {
219             Log.error(e.getMessage(), e);
220         }
221         sleep();
222     }
223 
getManager()224     public WebManager getManager() {
225         return this;
226     }
227 
validateService()228     public void validateService() {
229         if (getPresenceManager() == null ||
230                 getXMPPServer() == null) {
231             showServerDown();
232         }
233     }
234 
isServerRunning()235     public boolean isServerRunning() {
236         return !(getPresenceManager() == null || getXMPPServer() == null);
237     }
238 
setStart(int start)239     public void setStart(int start) {
240         this.start = start;
241     }
242 
getStart()243     public int getStart() {
244         return start;
245     }
246 
setRange(int range)247     public void setRange(int range) {
248         this.range = range;
249     }
250 
getRange()251     public int getRange() {
252         return range;
253     }
254 
getCurrentPage()255     public int getCurrentPage() {
256         return (start / range) + 1;
257     }
258 
sleep()259     private void sleep() {
260         // Sleep for a minute:
261         try {
262             Thread.sleep(3000L);
263         }
264         catch (Exception ignored) {
265             Log.trace("Sleep got interrupted.");
266         }
267     }
268 
showServerDown()269     protected void showServerDown() {
270         try {
271             response.sendRedirect("error-serverdown.jsp");
272         }
273         catch (Exception ex) {
274             ex.printStackTrace();
275         }
276     }
277 
278     /**
279      * Copies the contents at <CODE>src</CODE> to <CODE>dst</CODE>.
280      * @param src the source location
281      * @param dst the target location
282      * @throws IOException if the copy failed
283      */
copy(URL src, File dst)284     public static void copy(URL src, File dst) throws IOException {
285 
286         try (InputStream in = src.openStream()) {
287             try (OutputStream out = new FileOutputStream(dst)) {
288                 dst.mkdirs();
289                 copy(in, out);
290             }
291         }
292     }
293 
294     /**
295      * Common code for copy routines.  By convention, the streams are
296      * closed in the same method in which they were opened.  Thus,
297      * this method does not close the streams when the copying is done.
298      * @param in the input stream
299      * @param out the output stream
300      * @throws IOException if the copy failed
301      */
copy(InputStream in, OutputStream out)302     private static void copy(InputStream in, OutputStream out) throws IOException {
303         byte[] buffer = new byte[4096];
304         while (true) {
305             int bytesRead = in.read(buffer);
306             if (bytesRead < 0) {
307                 break;
308             }
309             out.write(buffer, 0, bytesRead);
310         }
311     }
312 
313     /**
314      * Returns the number of rows per page for the specified page for the current logged user.
315      * The rows per page value is stored as a user property. The same property is being used for
316      * different pages. The encoding format is the following "pageName1=value,pageName2=value".
317      *
318      * @param pageName     the name of the page to look up its stored value.
319      * @param defaultValue the default value to return if no user value was found.
320      * @return the number of rows per page for the specified page for the current logged user.
321      */
getRowsPerPage(String pageName, int defaultValue)322     public int getRowsPerPage(String pageName, int defaultValue) {
323         return getPageProperty(pageName, "console.rows_per_page", defaultValue);
324     }
325 
326     /**
327      * Sets the new number of rows per page for the specified page for the current logged user.
328      * The rows per page value is stored as a user property. The same property is being used for
329      * different pages. The encoding format is the following "pageName1=value,pageName2=value".
330      *
331      * @param pageName the name of the page to stored its new value.
332      * @param newValue the new rows per page value.
333      */
setRowsPerPage(String pageName, int newValue)334     public void setRowsPerPage(String pageName, int newValue) {
335         setPageProperty(pageName, "console.rows_per_page", newValue);
336     }
337 
338     /**
339      * Returns the number of seconds between each page refresh for the specified page for the
340      * current logged user. The value is stored as a user property. The same property is being
341      * used for different pages. The encoding format is the following
342      * "pageName1=value,pageName2=value".
343      *
344      * @param pageName     the name of the page to look up its stored value.
345      * @param defaultValue the default value to return if no user value was found.
346      * @return the number of seconds between each page refresh for the specified page for
347      *         the current logged user.
348      */
getRefreshValue(String pageName, int defaultValue)349     public int getRefreshValue(String pageName, int defaultValue) {
350         return getPageProperty(pageName, "console.refresh", defaultValue);
351     }
352 
353     /**
354      * Sets the number of seconds between each page refresh for the specified page for the
355      * current logged user. The value is stored as a user property. The same property is being
356      * used for different pages. The encoding format is the following
357      * "pageName1=value,pageName2=value".
358      *
359      * @param pageName the name of the page to stored its new value.
360      * @param newValue the new number of seconds between each page refresh.
361      */
setRefreshValue(String pageName, int newValue)362     public void setRefreshValue(String pageName, int newValue) {
363         setPageProperty(pageName, "console.refresh", newValue);
364     }
365 
getPageProperty(String pageName, String property, int defaultValue)366     public int getPageProperty(String pageName, String property, int defaultValue) {
367         User user = getUser();
368         if (user != null) {
369             String values = user.getProperties().get(property);
370             if (values != null) {
371                 StringTokenizer tokens = new StringTokenizer(values, ",=");
372                 while (tokens.hasMoreTokens()) {
373                     String page = tokens.nextToken().trim();
374                     String rows = tokens.nextToken().trim();
375                     if  (pageName.equals(page)) {
376                         try {
377                             return Integer.parseInt(rows);
378                         }
379                         catch (NumberFormatException e) {
380                             return defaultValue;
381                         }
382                     }
383                 }
384             }
385         }
386         return defaultValue;
387     }
388 
setPageProperty(String pageName, String property, int newValue)389     public void setPageProperty(String pageName, String property, int newValue) {
390         String toStore = pageName + "=" + newValue;
391         User user = getUser();
392         if (user != null) {
393             String values = user.getProperties().get(property);
394             if (values != null) {
395                 if (values.contains(toStore)) {
396                     // The new value for the page was already stored so do nothing
397                     return;
398                 }
399                 else {
400                     if (values.contains(pageName)) {
401                         // Replace an old value for the page with the new value
402                         int oldValue = getPageProperty(pageName, property, -1);
403                         String toRemove = pageName + "=" + oldValue;
404                         user.getProperties().put(property, values.replace(toRemove, toStore));
405                     }
406                     else {
407                         // Append the new page-value
408                         user.getProperties().put(property, values + "," + toStore);
409                     }
410                 }
411             }
412             else {
413                 // Store the new page-value as a new user property
414                 user.getProperties().put(property, toStore);
415             }
416         }
417     }
418 
getCaches()419     public Cache[] getCaches() {
420         Cache[] caches =CacheFactory.getAllCaches();
421         Arrays.sort(caches, new Comparator<Cache>() {
422             @Override
423             public int compare(Cache cache1, Cache cache2) {
424                 return cache1.getName().toLowerCase().compareTo(cache2.getName().toLowerCase());
425             }
426         });
427         return caches;
428     }
429 }
430